<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>凌霄的播客</title>
  
  <subtitle>你好，很高兴认识你</subtitle>
  <link href="http://example.com/atom.xml" rel="self"/>
  
  <link href="http://example.com/"/>
  <updated>2025-09-12T12:48:21.236Z</updated>
  <id>http://example.com/</id>
  
  <author>
    <name>lingxiao</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>Hello World</title>
    <link href="http://example.com/2025/09/12/hello-world/"/>
    <id>http://example.com/2025/09/12/hello-world/</id>
    <published>2025-09-12T12:10:46.444Z</published>
    <updated>2025-09-12T12:48:21.236Z</updated>
    
    <content type="html"><![CDATA[<p>Welcome to <a href="https://hexo.io/">Hexo</a>! This is your very first post. Check <a href="https://hexo.io/docs/">documentation</a> for more info. If you get any problems when using Hexo, you can find the answer in <a href="https://hexo.io/docs/troubleshooting.html">troubleshooting</a> or you can ask me on <a href="https://github.com/hexojs/hexo/issues">GitHub</a>.</p><h2 id="Quick-Start"><a href="#Quick-Start" class="headerlink" title="Quick Start"></a>Quick Start</h2><h3 id="Create-a-new-post"><a href="#Create-a-new-post" class="headerlink" title="Create a new post"></a>Create a new post</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo new <span class="string">&quot;My New Post&quot;</span></span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/writing.html">Writing</a></p><h3 id="Run-server"><a href="#Run-server" class="headerlink" title="Run server"></a>Run server</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo server</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/server.html">Server</a></p><h3 id="Generate-static-files"><a href="#Generate-static-files" class="headerlink" title="Generate static files"></a>Generate static files</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo generate</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/generating.html">Generating</a></p><h3 id="Deploy-to-remote-sites"><a href="#Deploy-to-remote-sites" class="headerlink" title="Deploy to remote sites"></a>Deploy to remote sites</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo deploy</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/one-command-deployment.html">Deployment</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;Welcome to &lt;a href=&quot;https://hexo.io/&quot;&gt;Hexo&lt;/a&gt;! This is your very first post. Check &lt;a href=&quot;https://hexo.io/docs/&quot;&gt;documentation&lt;/a&gt; for</summary>
      
    
    
    
    
  </entry>
  
  <entry>
    <title>从零开始学习springCloud</title>
    <link href="http://example.com/2022/01/20/%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E5%AD%A6%E4%B9%A0springCloud/"/>
    <id>http://example.com/2022/01/20/%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E5%AD%A6%E4%B9%A0springCloud/</id>
    <published>2022-01-20T07:18:53.000Z</published>
    <updated>2025-09-15T07:26:55.180Z</updated>
    
    <content type="html"><![CDATA[<h1 id="0-学习目标"><a href="#0-学习目标" class="headerlink" title="0.学习目标"></a>0.学习目标</h1><ul><li>了解系统架构的演变</li><li>了解RPC与Http的区别</li><li>知道什么是SpringCloud</li><li>独立搭建Eureka注册中心</li><li>独立配置Robbin负载均衡</li></ul><h1 id="1-系统架构演变"><a href="#1-系统架构演变" class="headerlink" title="1.系统架构演变"></a>1.系统架构演变</h1><p>随着互联网的发展，网站应用的规模不断扩大。需求的激增，带来的是技术上的压力。系统架构也因此不断的演进、升级、迭代。从单一应用，到垂直拆分，到分布式服务，到SOA，以及现在火热的微服务架构，还有在Google带领下来势汹涌的Service Mesh。我们到底是该乘坐微服务的船只驶向远方，还是偏安一隅得过且过？</p><p>其实生活不止眼前的苟且，还有诗和远方。所以我们今天就回顾历史，看一看系统架构演变的历程；把握现在，学习现在最火的技术架构；展望未来，争取成为一名优秀的Java工程师。</p><h2 id="1-1-集中式架构"><a href="#1-1-集中式架构" class="headerlink" title="1.1.集中式架构"></a>1.1.集中式架构</h2><p>当网站流量很小时，只需一个应用，将所有功能都部署在一起，以减少部署节点和成本。此时，用于简化增删改查工作量的数据访问框架(ORM)是影响项目开发的关键。</p><p><img src="/assets/1525529091749.png" alt="1525529091749"></p><p>存在的问题：</p><ul><li>代码耦合，开发维护困难</li><li>无法针对不同模块进行针对性优化</li><li>无法水平扩展</li><li>单点容错率低，并发能力差</li></ul><h2 id="1-2-垂直拆分"><a href="#1-2-垂直拆分" class="headerlink" title="1.2.垂直拆分"></a>1.2.垂直拆分</h2><p>当访问量逐渐增大，单一应用无法满足需求，此时为了应对更高的并发和业务需求，我们根据业务功能对系统进行拆分：</p><p><img src="/assets/1525529671801.png" alt="1525529671801"></p><p>优点：</p><ul><li>系统拆分实现了流量分担，解决了并发问题</li><li>可以针对不同模块进行优化</li><li>方便水平扩展，负载均衡，容错率提高</li></ul><p>缺点：</p><ul><li>系统间相互独立，会有很多重复开发工作，影响开发效率</li></ul><h2 id="1-3-分布式服务"><a href="#1-3-分布式服务" class="headerlink" title="1.3.分布式服务"></a>1.3.分布式服务</h2><p>当垂直应用越来越多，应用之间交互不可避免，将核心业务抽取出来，作为独立的服务，逐渐形成稳定的服务中心，使前端应用能更快速的响应多变的市场需求。此时，用于提高业务复用及整合的分布式调用是关键。</p><p><img src="/assets/1525530657919.png" alt="1525530657919"></p><p>优点：</p><ul><li>将基础服务进行了抽取，系统间相互调用，提高了代码复用和开发效率</li></ul><p>缺点：</p><ul><li>系统间耦合度变高，调用关系错综复杂，难以维护</li></ul><h2 id="1-4-流动计算架构（SOA）"><a href="#1-4-流动计算架构（SOA）" class="headerlink" title="1.4.流动计算架构（SOA）"></a>1.4.流动计算架构（SOA）</h2><p>SOA ：面向服务的架构</p><p>当服务越来越多，容量的评估，小服务资源的浪费等问题逐渐显现，此时需增加一个调度中心基于访问压力实时管理集群容量，提高集群利用率。此时，用于提高机器利用率的资源调度和治理中心(SOA)是关键</p><p><img src="/assets/1525530804753.png" alt="1525530804753"></p><p>以前出现了什么问题？</p><ul><li>服务越来越多，需要管理每个服务的地址</li><li>调用关系错综复杂，难以理清依赖关系</li><li>服务过多，服务状态难以管理，无法根据服务情况动态管理</li></ul><p>服务治理要做什么？</p><ul><li>服务注册中心，实现服务自动注册和发现，无需人为记录服务地址</li><li>服务自动订阅，服务列表自动推送，服务调用透明化，无需关心依赖关系</li><li>动态监控服务状态监控报告，人为控制服务状态</li></ul><p>缺点：</p><ul><li>服务间会有依赖关系，一旦某个环节出错会影响较大</li><li>服务关系复杂，运维、测试部署困难，不符合DevOps思想</li></ul><h2 id="1-5-微服务"><a href="#1-5-微服务" class="headerlink" title="1.5.微服务"></a>1.5.微服务</h2><p>前面说的SOA，英文翻译过来是面向服务。微服务，似乎也是服务，都是对系统进行拆分。因此两者非常容易混淆，但其实却有一些差别：</p><p>微服务的特点：</p><ul><li>单一职责：微服务中每一个服务都对应唯一的业务能力，做到单一职责</li><li>微：微服务的服务拆分粒度很小，例如一个用户管理就可以作为一个服务。每个服务虽小，但“五脏俱全”。</li><li>面向服务：面向服务是说每个服务都要对外暴露Rest风格服务接口API。并不关心服务的技术实现，做到与平台和语言无关，也不限定用什么技术实现，只要提供Rest的接口即可。</li><li>自治：自治是说服务间互相独立，互不干扰<ul><li>团队独立：每个服务都是一个独立的开发团队，人数不能过多。</li><li>技术独立：因为是面向服务，提供Rest接口，使用什么技术没有别人干涉</li><li>前后端分离：采用前后端分离开发，提供统一Rest接口，后端不用再为PC、移动段开发不同接口</li><li>数据库分离：每个服务都使用自己的数据源</li><li>部署独立，服务间虽然有调用，但要做到服务重启不影响其它服务。有利于持续集成和持续交付。每个服务都是独立的组件，可复用，可替换，降低耦合，易维护</li></ul></li></ul><p>微服务结构图：</p><p><img src="/assets/1526860071166.png" alt="1526860071166"></p><h1 id="2-服务调用方式"><a href="#2-服务调用方式" class="headerlink" title="2.服务调用方式"></a>2.服务调用方式</h1><h2 id="2-1-RPC和HTTP"><a href="#2-1-RPC和HTTP" class="headerlink" title="2.1.RPC和HTTP"></a>2.1.RPC和HTTP</h2><p>无论是微服务还是SOA，都面临着服务间的远程调用。那么服务间的远程调用方式有哪些呢？</p><p>常见的远程调用方式有以下2种：</p><ul><li><p>RPC：Remote Produce Call远程过程调用，类似的还有RMI。自定义数据格式，基于原生TCP通信，速度快，效率高。早期的webservice，现在热门的dubbo，都是RPC的典型代表</p></li><li><p>Http：http其实是一种网络传输协议，基于TCP，规定了数据传输的格式。现在客户端浏览器与服务端通信基本都是采用Http协议，也可以用来进行远程服务调用。缺点是消息封装臃肿，优势是对服务的提供和调用方没有任何技术限定，自由灵活，更符合微服务理念。</p><p>现在热门的Rest风格，就可以通过http协议来实现。</p></li></ul><p>如果你们公司全部采用Java技术栈，那么使用Dubbo作为微服务架构是一个不错的选择。</p><p>相反，如果公司的技术栈多样化，而且你更青睐Spring家族，那么SpringCloud搭建微服务是不二之选。在我们的项目中，我们会选择SpringCloud套件，因此我们会使用Http方式来实现服务间调用。</p><h2 id="2-2-Http客户端工具"><a href="#2-2-Http客户端工具" class="headerlink" title="2.2.Http客户端工具"></a>2.2.Http客户端工具</h2><p>既然微服务选择了Http，那么我们就需要考虑自己来实现对请求和响应的处理。不过开源世界已经有很多的http客户端工具，能够帮助我们做这些事情，例如：</p><ul><li>HttpClient</li><li>OKHttp</li><li>URLConnection</li></ul><p>接下来，不过这些不同的客户端，API各不相同</p><h2 id="2-3-Spring的RestTemplate"><a href="#2-3-Spring的RestTemplate" class="headerlink" title="2.3.Spring的RestTemplate"></a>2.3.Spring的RestTemplate</h2><p>Spring提供了一个RestTemplate模板工具类，对基于Http的客户端进行了封装，并且实现了对象与json的序列化和反序列化，非常方便。RestTemplate并没有限定Http的客户端类型，而是进行了抽象，目前常用的3种都有支持：</p><ul><li>HttpClient</li><li>OkHttp</li><li>JDK原生的URLConnection（默认的）</li></ul><p>我们导入课前资料提供的demo工程：</p><p><img src="/assets/1534812923451.png" alt="1534812923451"></p><p>首先在项目中注册一个<code>RestTemplate</code>对象，可以在启动类位置注册：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HttpDemoApplication</span> &#123;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">SpringApplication.run(HttpDemoApplication.class, args);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="keyword">public</span> RestTemplate <span class="title function_">restTemplate</span><span class="params">()</span> &#123;</span><br><span class="line">   </span><br><span class="line"><span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">RestTemplate</span>();</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在测试类中直接<code>@Autowired</code>注入：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@RunWith(SpringRunner.class)</span></span><br><span class="line"><span class="meta">@SpringBootTest(classes = HttpDemoApplication.class)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HttpDemoApplicationTests</span> &#123;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Autowired</span></span><br><span class="line"><span class="keyword">private</span> RestTemplate restTemplate;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">httpGet</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 调用springboot案例中的rest接口</span></span><br><span class="line"><span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> <span class="built_in">this</span>.restTemplate.getForObject(<span class="string">&quot;http://localhost/user/1&quot;</span>, User.class);</span><br><span class="line">System.out.println(user);</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>通过RestTemplate的getForObject()方法，传递url地址及实体类的字节码，RestTemplate会自动发起请求，接收响应，并且帮我们对响应结果进行反序列化。</li></ul><p><img src="/assets/1525573702492.png" alt="1525573702492"></p><p>学习完了Http客户端工具，接下来就可以正式学习微服务了。</p><h1 id="3-初识SpringCloud"><a href="#3-初识SpringCloud" class="headerlink" title="3.初识SpringCloud"></a>3.初识SpringCloud</h1><p>微服务是一种架构方式，最终肯定需要技术架构去实施。</p><p>微服务的实现方式很多，但是最火的莫过于Spring Cloud了。为什么？</p><ul><li>后台硬：作为Spring家族的一员，有整个Spring全家桶靠山，背景十分强大。</li><li>技术强：Spring作为Java领域的前辈，可以说是功力深厚。有强力的技术团队支撑，一般人还真比不了</li><li>群众基础好：可以说大多数程序员的成长都伴随着Spring框架，试问：现在有几家公司开发不用Spring？SpringCloud与Spring的各个框架无缝整合，对大家来说一切都是熟悉的配方，熟悉的味道。</li><li>使用方便：相信大家都体会到了SpringBoot给我们开发带来的便利，而SpringCloud完全支持SpringBoot的开发，用很少的配置就能完成微服务框架的搭建</li></ul><h2 id="3-1-简介"><a href="#3-1-简介" class="headerlink" title="3.1.简介"></a>3.1.简介</h2><p>SpringCloud是Spring旗下的项目之一，<a href="http://projects.spring.io/spring-cloud/">官网地址：http://projects.spring.io/spring-cloud/</a></p><p>Spring最擅长的就是集成，把世界上最好的框架拿过来，集成到自己的项目中。</p><p>SpringCloud也是一样，它将现在非常流行的一些技术整合到一起，实现了诸如：配置管理，服务发现，智能路由，负载均衡，熔断器，控制总线，集群状态等等功能。其主要涉及的组件包括：</p><ul><li>Eureka：服务治理组件，包含服务注册中心，服务注册与发现机制的实现。（服务治理，服务注册&#x2F;发现）</li><li>Zuul：网关组件，提供智能路由，访问过滤功能</li><li>Ribbon：客户端负载均衡的服务调用组件（客户端负载）</li><li>Feign：服务调用，给予Ribbon和Hystrix的声明式服务调用组件 （声明式服务调用）</li><li>Hystrix：容错管理组件，实现断路器模式，帮助服务依赖中出现的延迟和为故障提供强大的容错能力。(熔断、断路器，容错)</li></ul><p>架构图：</p><p><img src="/assets/1525575656796.png" alt="1525575656796"></p><p>以上只是其中一部分。</p><h2 id="3-2-版本"><a href="#3-2-版本" class="headerlink" title="3.2.版本"></a>3.2.版本</h2><p>因为Spring Cloud不同其他独立项目，它拥有很多子项目的大项目。所以它的版本是版本名+版本号 （如Angel.SR6）。</p><p>版本名：是伦敦的地铁名</p><p>版本号：SR（Service Releases）是固定的 ,大概意思是稳定版本。后面会有一个递增的数字。</p><p>所以 Edgware.SR3就是Edgware的第3个Release版本。</p><p><img src="/assets/1528263985902.png" alt="1528263985902"></p><p>我们在项目中，会是以Finchley的版本。</p><p>其中包含的组件，也都有各自的版本，如下表：</p><table><thead><tr><th>Component</th><th>Edgware.SR3</th><th>Finchley.RC1</th><th>Finchley.BUILD-SNAPSHOT</th></tr></thead><tbody><tr><td>spring-cloud-aws</td><td>1.2.2.RELEASE</td><td>2.0.0.RC1</td><td>2.0.0.BUILD-SNAPSHOT</td></tr><tr><td>spring-cloud-bus</td><td>1.3.2.RELEASE</td><td>2.0.0.RC1</td><td>2.0.0.BUILD-SNAPSHOT</td></tr><tr><td>spring-cloud-cli</td><td>1.4.1.RELEASE</td><td>2.0.0.RC1</td><td>2.0.0.BUILD-SNAPSHOT</td></tr><tr><td>spring-cloud-commons</td><td>1.3.3.RELEASE</td><td>2.0.0.RC1</td><td>2.0.0.BUILD-SNAPSHOT</td></tr><tr><td>spring-cloud-contract</td><td>1.2.4.RELEASE</td><td>2.0.0.RC1</td><td>2.0.0.BUILD-SNAPSHOT</td></tr><tr><td>spring-cloud-config</td><td>1.4.3.RELEASE</td><td>2.0.0.RC1</td><td>2.0.0.BUILD-SNAPSHOT</td></tr><tr><td>spring-cloud-netflix</td><td>1.4.4.RELEASE</td><td>2.0.0.RC1</td><td>2.0.0.BUILD-SNAPSHOT</td></tr><tr><td>spring-cloud-security</td><td>1.2.2.RELEASE</td><td>2.0.0.RC1</td><td>2.0.0.BUILD-SNAPSHOT</td></tr><tr><td>spring-cloud-cloudfoundry</td><td>1.1.1.RELEASE</td><td>2.0.0.RC1</td><td>2.0.0.BUILD-SNAPSHOT</td></tr><tr><td>spring-cloud-consul</td><td>1.3.3.RELEASE</td><td>2.0.0.RC1</td><td>2.0.0.BUILD-SNAPSHOT</td></tr><tr><td>spring-cloud-sleuth</td><td>1.3.3.RELEASE</td><td>2.0.0.RC1</td><td>2.0.0.BUILD-SNAPSHOT</td></tr><tr><td>spring-cloud-stream</td><td>Ditmars.SR3</td><td>Elmhurst.RELEASE</td><td>Elmhurst.BUILD-SNAPSHOT</td></tr><tr><td>spring-cloud-zookeeper</td><td>1.2.1.RELEASE</td><td>2.0.0.RC1</td><td>2.0.0.BUILD-SNAPSHOT</td></tr><tr><td>spring-boot</td><td>1.5.10.RELEASE</td><td>2.0.1.RELEASE</td><td>2.0.0.BUILD-SNAPSHOT</td></tr><tr><td>spring-cloud-task</td><td>1.2.2.RELEASE</td><td>2.0.0.RC1</td><td>2.0.0.RELEASE</td></tr><tr><td>spring-cloud-vault</td><td>1.1.0.RELEASE</td><td>2.0.0.RC1</td><td>2.0.0.BUILD-SNAPSHOT</td></tr><tr><td>spring-cloud-gateway</td><td>1.0.1.RELEASE</td><td>2.0.0.RC1</td><td>2.0.0.BUILD-SNAPSHOT</td></tr><tr><td>spring-cloud-openfeign</td><td></td><td>2.0.0.RC1</td><td>2.0.0.BUILD-SNAPSHOT</td></tr></tbody></table><p><img src="/assets/1528263942152.png" alt="1528263942152"></p><p>接下来，我们就一一学习SpringCloud中的重要组件。</p><h1 id="4-微服务场景模拟"><a href="#4-微服务场景模拟" class="headerlink" title="4.微服务场景模拟"></a>4.微服务场景模拟</h1><p>首先，我们需要模拟一个服务调用的场景，搭建两个工程：itcast-service-provider（服务提供方）和itcast-service-consumer（服务调用方）。方便后面学习微服务架构</p><p>服务提供方：使用mybatis操作数据库，实现对数据的增删改查；并对外提供rest接口服务。</p><p>服务消费方：使用restTemplate远程调用服务提供方的rest接口服务，获取数据。</p><h2 id="4-1-服务提供者"><a href="#4-1-服务提供者" class="headerlink" title="4.1.服务提供者"></a>4.1.服务提供者</h2><p>我们新建一个项目：itcast-service-provider，对外提供根据id查询用户的服务。</p><h3 id="4-1-1-Spring脚手架创建工程"><a href="#4-1-1-Spring脚手架创建工程" class="headerlink" title="4.1.1.Spring脚手架创建工程"></a>4.1.1.Spring脚手架创建工程</h3><p>借助于Spring提供的快速搭建工具：</p><p><img src="/assets/1528266515225.png" alt="1528266515225"></p><p>next–&gt;填写项目信息：</p><p><img src="/assets/1528266803455.png" alt="1528266803455"></p><p>next –&gt;  添加web依赖：</p><p><img src="/assets/1528266923967.png" alt="1528266923967"></p><p>添加mybatis依赖：</p><p><img src="/assets/1528267588935.png" alt="1528267588935"></p><p>Next –&gt;  填写项目位置：</p><p><img src="/assets/1528267665004.png" alt="1528267665004"></p><p>生成的项目结构，已经包含了引导类（itcastServiceProviderApplication）：</p><p><img src="/assets/1528267830854.png" alt="1528267830854"></p><p>依赖也已经全部自动引入：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">project</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://maven.apache.org/POM/4.0.0&quot;</span> <span class="attr">xmlns:xsi</span>=<span class="string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">modelVersion</span>&gt;</span>4.0.0<span class="tag">&lt;/<span class="name">modelVersion</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>cn.itcast.service<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>itcast-service-provider<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>0.0.1-SNAPSHOT<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">packaging</span>&gt;</span>jar<span class="tag">&lt;/<span class="name">packaging</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">name</span>&gt;</span>itcast-service-provider<span class="tag">&lt;/<span class="name">name</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">description</span>&gt;</span>Demo project for Spring Boot<span class="tag">&lt;/<span class="name">description</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">parent</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-parent<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.0.6.RELEASE<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">relativePath</span>/&gt;</span> <span class="comment">&lt;!-- lookup parent from repository --&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">parent</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">properties</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">project.build.sourceEncoding</span>&gt;</span>UTF-8<span class="tag">&lt;/<span class="name">project.build.sourceEncoding</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">project.reporting.outputEncoding</span>&gt;</span>UTF-8<span class="tag">&lt;/<span class="name">project.reporting.outputEncoding</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">java.version</span>&gt;</span>1.8<span class="tag">&lt;/<span class="name">java.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">properties</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-jdbc<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-web<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.mybatis.spring.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>mybatis-spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.3.2<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>mysql<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>mysql-connector-java<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">scope</span>&gt;</span>runtime<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-test<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">scope</span>&gt;</span>test<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 需要手动引入通用mapper的启动器，spring没有收录该依赖 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>tk.mybatis<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>mapper-spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.0.4<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">build</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">plugins</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">plugin</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-maven-plugin<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">plugin</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">plugins</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">build</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">project</span>&gt;</span></span><br></pre></td></tr></table></figure><p>当然，因为要使用通用mapper，所以我们<strong>需要手动加一条依赖</strong>：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>tk.mybatis<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>mapper-spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.0.4<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><p>非常快捷啊！</p><h3 id="4-1-2-编写代码"><a href="#4-1-2-编写代码" class="headerlink" title="4.1.2.编写代码"></a>4.1.2.编写代码</h3><p><img src="/assets/1535671376231.png" alt="1535671376231"></p><h4 id="4-1-2-1-配置"><a href="#4-1-2-1-配置" class="headerlink" title="4.1.2.1.配置"></a>4.1.2.1.配置</h4><p>属性文件,这里我们采用了yaml语法，而不是properties：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">server:</span></span><br><span class="line">  <span class="attr">port:</span> <span class="number">8081</span></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">datasource:</span></span><br><span class="line">    <span class="attr">url:</span> <span class="string">jdbc:mysql://localhost:3306/mybatis</span> <span class="comment">#你学习mybatis时，使用的数据库地址</span></span><br><span class="line">    <span class="attr">username:</span> <span class="string">root</span></span><br><span class="line">    <span class="attr">password:</span> <span class="string">root</span></span><br><span class="line"><span class="attr">mybatis:</span></span><br><span class="line">  <span class="attr">type-aliases-package:</span> <span class="string">cn.itcast.service.pojo</span></span><br></pre></td></tr></table></figure><h4 id="4-1-2-2-实体类"><a href="#4-1-2-2-实体类" class="headerlink" title="4.1.2.2.实体类"></a>4.1.2.2.实体类</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Table(name = &quot;tb_user&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">User</span> <span class="keyword">implements</span> <span class="title class_">Serializable</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">long</span> <span class="variable">serialVersionUID</span> <span class="operator">=</span> <span class="number">1L</span>;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Id</span></span><br><span class="line">    <span class="meta">@GeneratedValue(strategy = GenerationType.IDENTITY)</span></span><br><span class="line">    <span class="keyword">private</span> Long id;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 用户名</span></span><br><span class="line">    <span class="keyword">private</span> String userName;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 密码</span></span><br><span class="line">    <span class="keyword">private</span> String password;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 姓名</span></span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 年龄</span></span><br><span class="line">    <span class="keyword">private</span> Integer age;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 性别，1男性，2女性</span></span><br><span class="line">    <span class="keyword">private</span> Integer sex;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 出生日期</span></span><br><span class="line">    <span class="keyword">private</span> Date birthday;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建时间</span></span><br><span class="line">    <span class="keyword">private</span> Date created;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 更新时间</span></span><br><span class="line">    <span class="keyword">private</span> Date updated;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Long <span class="title function_">getId</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> id;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setId</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.id = id;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getUserName</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> userName;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setUserName</span><span class="params">(String userName)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.userName = userName;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getPassword</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> password;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setPassword</span><span class="params">(String password)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.password = password;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getName</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> name;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setName</span><span class="params">(String name)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Integer <span class="title function_">getAge</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> age;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setAge</span><span class="params">(Integer age)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.age = age;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Integer <span class="title function_">getSex</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> sex;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setSex</span><span class="params">(Integer sex)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.sex = sex;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Date <span class="title function_">getBirthday</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> birthday;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setBirthday</span><span class="params">(Date birthday)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.birthday = birthday;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Date <span class="title function_">getCreated</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> created;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setCreated</span><span class="params">(Date created)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.created = created;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Date <span class="title function_">getUpdated</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> updated;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setUpdated</span><span class="params">(Date updated)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.updated = updated;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="4-1-2-3-UserMapper"><a href="#4-1-2-3-UserMapper" class="headerlink" title="4.1.2.3.UserMapper"></a>4.1.2.3.UserMapper</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Mapper</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">UserMapper</span> <span class="keyword">extends</span> <span class="title class_">tk</span>.mybatis.mapper.common.Mapper&lt;User&gt;&#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="4-1-2-4-UserService"><a href="#4-1-2-4-UserService" class="headerlink" title="4.1.2.4.UserService"></a>4.1.2.4.UserService</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserService</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> UserMapper userMapper;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> User <span class="title function_">queryById</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">this</span>.userMapper.selectByPrimaryKey(id);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="4-1-2-5-UserController"><a href="#4-1-2-5-UserController" class="headerlink" title="4.1.2.5.UserController"></a>4.1.2.5.UserController</h4><p>添加一个对外查询的接口：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;user&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserController</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> UserService userService;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@GetMapping(&quot;&#123;id&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> User <span class="title function_">queryById</span><span class="params">(<span class="meta">@PathVariable(&quot;id&quot;)</span> Long id)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">this</span>.userService.queryById(id);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="4-1-3-启动并测试"><a href="#4-1-3-启动并测试" class="headerlink" title="4.1.3.启动并测试"></a>4.1.3.启动并测试</h3><p>启动项目，访问接口：<a href="http://localhost:8081/user/1">http://localhost:8081/user/1</a></p><p><img src="/assets/1528272537927.png" alt="1528272537927"></p><h2 id="4-2-服务调用者"><a href="#4-2-服务调用者" class="headerlink" title="4.2.服务调用者"></a>4.2.服务调用者</h2><p>搭建itcast-service-consumer服务消费方工程。</p><h3 id="4-2-1-创建工程"><a href="#4-2-1-创建工程" class="headerlink" title="4.2.1.创建工程"></a>4.2.1.创建工程</h3><p>与上面类似，这里不再赘述，需要注意的是，我们调用itcast-service-provider的解耦获取数据，因此不需要mybatis相关依赖了。</p><p><img src="/assets/1528272829384.png" alt="1528272829384"></p><p><img src="/assets/1528272875502.png" alt="1528272875502"></p><p><img src="/assets/1528273029361.png" alt="1528273029361"></p><p>pom：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><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">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">project</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://maven.apache.org/POM/4.0.0&quot;</span> <span class="attr">xmlns:xsi</span>=<span class="string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">modelVersion</span>&gt;</span>4.0.0<span class="tag">&lt;/<span class="name">modelVersion</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>cn.itcast.service<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>itcast-service-consumer<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>0.0.1-SNAPSHOT<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">packaging</span>&gt;</span>jar<span class="tag">&lt;/<span class="name">packaging</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">name</span>&gt;</span>itcast-service-consumer<span class="tag">&lt;/<span class="name">name</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">description</span>&gt;</span>Demo project for Spring Boot<span class="tag">&lt;/<span class="name">description</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">parent</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-parent<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.0.4.RELEASE<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">relativePath</span>/&gt;</span> <span class="comment">&lt;!-- lookup parent from repository --&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">parent</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">properties</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">project.build.sourceEncoding</span>&gt;</span>UTF-8<span class="tag">&lt;/<span class="name">project.build.sourceEncoding</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">project.reporting.outputEncoding</span>&gt;</span>UTF-8<span class="tag">&lt;/<span class="name">project.reporting.outputEncoding</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">java.version</span>&gt;</span>1.8<span class="tag">&lt;/<span class="name">java.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">properties</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-web<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-test<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">scope</span>&gt;</span>test<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">build</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">plugins</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">plugin</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-maven-plugin<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">plugin</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">plugins</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">build</span>&gt;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">project</span>&gt;</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="4-2-2-编写代码"><a href="#4-2-2-编写代码" class="headerlink" title="4.2.2.编写代码"></a>4.2.2.编写代码</h3><p><img src="/assets/1540643087305.png" alt="1540643087305"></p><p>首先在引导类中注册<code>RestTemplate</code>：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ItcastServiceConsumerApplication</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> RestTemplate <span class="title function_">restTemplate</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">RestTemplate</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        SpringApplication.run(ItcastServiceConsumerApplication.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>编写配置（application.yml）：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">server:</span><br><span class="line">  port: 80</span><br></pre></td></tr></table></figure><p>编写UserController：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Controller</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;consumer/user&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserController</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> RestTemplate restTemplate;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@GetMapping</span></span><br><span class="line">    <span class="meta">@ResponseBody</span></span><br><span class="line">    <span class="keyword">public</span> User <span class="title function_">queryUserById</span><span class="params">(<span class="meta">@RequestParam(&quot;id&quot;)</span> Long id)</span>&#123;</span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> <span class="built_in">this</span>.restTemplate.getForObject(<span class="string">&quot;http://localhost:8081/user/&quot;</span> + id, User.class);</span><br><span class="line">        <span class="keyword">return</span> user;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>pojo对象（User）：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">User</span> <span class="keyword">implements</span> <span class="title class_">Serializable</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">long</span> <span class="variable">serialVersionUID</span> <span class="operator">=</span> <span class="number">1L</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Long id;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 用户名</span></span><br><span class="line">    <span class="keyword">private</span> String userName;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 密码</span></span><br><span class="line">    <span class="keyword">private</span> String password;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 姓名</span></span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 年龄</span></span><br><span class="line">    <span class="keyword">private</span> Integer age;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 性别，1男性，2女性</span></span><br><span class="line">    <span class="keyword">private</span> Integer sex;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 出生日期</span></span><br><span class="line">    <span class="keyword">private</span> Date birthday;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建时间</span></span><br><span class="line">    <span class="keyword">private</span> Date created;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 更新时间</span></span><br><span class="line">    <span class="keyword">private</span> Date updated;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Long <span class="title function_">getId</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> id;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setId</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.id = id;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getUserName</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> userName;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setUserName</span><span class="params">(String userName)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.userName = userName;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getPassword</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> password;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setPassword</span><span class="params">(String password)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.password = password;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getName</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> name;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setName</span><span class="params">(String name)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Integer <span class="title function_">getAge</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> age;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setAge</span><span class="params">(Integer age)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.age = age;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Integer <span class="title function_">getSex</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> sex;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setSex</span><span class="params">(Integer sex)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.sex = sex;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Date <span class="title function_">getBirthday</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> birthday;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setBirthday</span><span class="params">(Date birthday)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.birthday = birthday;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Date <span class="title function_">getCreated</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> created;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setCreated</span><span class="params">(Date created)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.created = created;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Date <span class="title function_">getUpdated</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> updated;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setUpdated</span><span class="params">(Date updated)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.updated = updated;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="4-2-3-启动测试"><a href="#4-2-3-启动测试" class="headerlink" title="4.2.3.启动测试"></a>4.2.3.启动测试</h3><p>因为我们没有配置端口，那么默认就是8080，我们访问：<a href="http://localhost/consumer/user?id=1">http://localhost/consumer/user?id=1</a></p><p><img src="/assets/1535587919022.png" alt="1535587919022"></p><p>一个简单的远程服务调用案例就实现了。</p><h2 id="4-3-有没有问题？"><a href="#4-3-有没有问题？" class="headerlink" title="4.3.有没有问题？"></a>4.3.有没有问题？</h2><p>简单回顾一下，刚才我们写了什么：</p><ul><li>itcast-service-provider：一个提供根据id查询用户的微服务。</li><li>itcast-service-consumer：一个服务调用者，通过RestTemplate远程调用itcast-service-provider。</li></ul><p>存在什么问题？</p><ul><li>在consumer中，我们把url地址硬编码到了代码中，不方便后期维护</li><li>consumer需要记忆provider的地址，如果出现变更，可能得不到通知，地址将失效</li><li>consumer不清楚provider的状态，服务宕机也不知道</li><li>provider只有1台服务，不具备高可用性</li><li>即便provider形成集群，consumer还需自己实现负载均衡</li></ul><p>其实上面说的问题，概括一下就是分布式服务必然要面临的问题：</p><ul><li>服务管理<ul><li>如何自动注册和发现</li><li>如何实现状态监管</li><li>如何实现动态路由</li></ul></li><li>服务如何实现负载均衡</li><li>服务如何解决容灾问题</li><li>服务如何实现统一配置</li></ul><p>以上的问题，我们都将在SpringCloud中得到答案。</p><h1 id="5-Eureka注册中心"><a href="#5-Eureka注册中心" class="headerlink" title="5.Eureka注册中心"></a>5.Eureka注册中心</h1><h2 id="5-1-认识Eureka"><a href="#5-1-认识Eureka" class="headerlink" title="5.1.认识Eureka"></a>5.1.认识Eureka</h2><p>首先我们来解决第一问题，服务的管理。</p><blockquote><p>问题分析</p></blockquote><p>在刚才的案例中，itcast-service-provider对外提供服务，需要对外暴露自己的地址。而consumer（调用者）需要记录服务提供者的地址。将来地址出现变更，还需要及时更新。这在服务较少的时候并不觉得有什么，但是在现在日益复杂的互联网环境，一个项目肯定会拆分出十几，甚至数十个微服务。此时如果还人为管理地址，不仅开发困难，将来测试、发布上线都会非常麻烦，这与DevOps的思想是背道而驰的。</p><blockquote><p>网约车</p></blockquote><p>这就好比是 网约车出现以前，人们出门叫车只能叫出租车。一些私家车想做出租却没有资格，被称为黑车。而很多人想要约车，但是无奈出租车太少，不方便。私家车很多却不敢拦，而且满大街的车，谁知道哪个才是愿意载人的。一个想要，一个愿意给，就是缺少引子，缺乏管理啊。</p><p>此时滴滴这样的网约车平台出现了，所有想载客的私家车全部到滴滴注册，记录你的车型（服务类型），身份信息（联系方式）。这样提供服务的私家车，在滴滴那里都能找到，一目了然。</p><p>此时要叫车的人，只需要打开APP，输入你的目的地，选择车型（服务类型），滴滴自动安排一个符合需求的车到你面前，为你服务，完美！</p><blockquote><p>Eureka做什么？</p></blockquote><p>Eureka就好比是滴滴，负责管理、记录服务提供者的信息。服务调用者无需自己寻找服务，而是把自己的需求告诉Eureka，然后Eureka会把符合你需求的服务告诉你。</p><p>同时，服务提供方与Eureka之间通过<code>“心跳”</code>机制进行监控，当某个服务提供方出现问题，Eureka自然会把它从服务列表中剔除。</p><p>这就实现了服务的自动注册、发现、状态监控。</p><h2 id="5-2-原理图"><a href="#5-2-原理图" class="headerlink" title="5.2.原理图"></a>5.2.原理图</h2><blockquote><p>基本架构：</p></blockquote><p><img src="/assets/1525597885059.png" alt="1525597885059"></p><ul><li>Eureka：就是服务注册中心（可以是一个集群），对外暴露自己的地址</li><li>提供者：启动后向Eureka注册自己信息（地址，提供什么服务）</li><li>消费者：向Eureka订阅服务，Eureka会将对应服务的所有提供者地址列表发送给消费者，并且定期更新</li><li>心跳(续约)：提供者定期通过http方式向Eureka刷新自己的状态</li></ul><h2 id="5-3-入门案例"><a href="#5-3-入门案例" class="headerlink" title="5.3.入门案例"></a>5.3.入门案例</h2><h3 id="5-3-1-搭建EurekaServer"><a href="#5-3-1-搭建EurekaServer" class="headerlink" title="5.3.1.搭建EurekaServer"></a>5.3.1.搭建EurekaServer</h3><p>接下来我们创建一个项目，启动一个EurekaServer：</p><p>依然使用spring提供的快速搭建工具：</p><p><img src="/assets/1528295484808.png" alt="1528295484808"></p><p>选择依赖：EurekaServer-服务注册中心依赖，Eureka Discovery-服务提供方和服务消费方。因为，对于eureka来说：服务提供方和服务消费方都属于客户端</p><p><img src="/assets/1528295612501.png" alt="1528295612501"></p><p><img src="/assets/1528295690203.png" alt="1528295690203"></p><p>完整的Pom文件：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">project</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://maven.apache.org/POM/4.0.0&quot;</span> <span class="attr">xmlns:xsi</span>=<span class="string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">modelVersion</span>&gt;</span>4.0.0<span class="tag">&lt;/<span class="name">modelVersion</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>cn.itcast.eureka<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>itcast-eureka<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>0.0.1-SNAPSHOT<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">packaging</span>&gt;</span>jar<span class="tag">&lt;/<span class="name">packaging</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">name</span>&gt;</span>itcast-eureka<span class="tag">&lt;/<span class="name">name</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">description</span>&gt;</span>Demo project for Spring Boot<span class="tag">&lt;/<span class="name">description</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">parent</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-parent<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.0.6.RELEASE<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">relativePath</span>/&gt;</span> <span class="comment">&lt;!-- lookup parent from repository --&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">parent</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">properties</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">project.build.sourceEncoding</span>&gt;</span>UTF-8<span class="tag">&lt;/<span class="name">project.build.sourceEncoding</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">project.reporting.outputEncoding</span>&gt;</span>UTF-8<span class="tag">&lt;/<span class="name">project.reporting.outputEncoding</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">java.version</span>&gt;</span>1.8<span class="tag">&lt;/<span class="name">java.version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">spring-cloud.version</span>&gt;</span>Finchley.RC2<span class="tag">&lt;/<span class="name">spring-cloud.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">properties</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.cloud<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-cloud-starter-netflix-eureka-server<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">build</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">plugins</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">plugin</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-maven-plugin<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">plugin</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">plugins</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">build</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">project</span>&gt;</span></span><br></pre></td></tr></table></figure><p>编写application.yml配置：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">server:</span></span><br><span class="line">  <span class="attr">port:</span> <span class="number">10086</span> <span class="comment"># 端口</span></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">application:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">eureka-server</span> <span class="comment"># 应用名称，会在Eureka中显示</span></span><br><span class="line"><span class="attr">eureka:</span></span><br><span class="line">  <span class="attr">client:</span></span><br><span class="line">    <span class="attr">service-url:</span> <span class="comment"># EurekaServer的地址，现在是自己的地址，如果是集群，需要加上其它Server的地址。</span></span><br><span class="line">      <span class="attr">defaultZone:</span> <span class="string">http://127.0.0.1:$&#123;server.port&#125;/eureka</span></span><br></pre></td></tr></table></figure><p>修改引导类，在类上添加@EnableEurekaServer注解：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="meta">@EnableEurekaServer</span> <span class="comment">// 声明当前springboot应用是一个eureka服务中心</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ItcastEurekaApplication</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        SpringApplication.run(ItcastEurekaApplication.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>启动服务，并访问：<a href="http://127.0.0.1:10086/">http://127.0.0.1:10086</a></p><p><img src="/assets/1525604959508.png" alt="1525604959508"></p><p><img src="/assets/1533793804268.png" alt="1533793804268"></p><h3 id="5-3-2-注册到Eureka"><a href="#5-3-2-注册到Eureka" class="headerlink" title="5.3.2.注册到Eureka"></a>5.3.2.注册到Eureka</h3><p>注册服务，就是在服务上添加Eureka的客户端依赖，客户端代码会自动把服务注册到EurekaServer中。</p><p>修改itcast-service-provider工程</p><ol><li>在pom.xml中，添加springcloud的相关依赖。</li><li>在application.yml中，添加springcloud的相关依赖。</li><li>在引导类上添加注解，把服务注入到eureka注册中心。</li></ol><p>具体操作</p><h4 id="5-3-2-1-pom-xml"><a href="#5-3-2-1-pom-xml" class="headerlink" title="5.3.2.1.pom.xml"></a>5.3.2.1.pom.xml</h4><p>参照itcast-eureka，先添加SpringCloud依赖：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- SpringCloud的依赖 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependencyManagement</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.cloud<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-cloud-dependencies<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>Finchley.SR2<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">type</span>&gt;</span>pom<span class="tag">&lt;/<span class="name">type</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">scope</span>&gt;</span>import<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependencyManagement</span>&gt;</span></span><br></pre></td></tr></table></figure><p>然后是Eureka客户端：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- Eureka客户端 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.cloud<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-cloud-starter-netflix-eureka-client<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><p>完整pom.xml:</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">project</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://maven.apache.org/POM/4.0.0&quot;</span> <span class="attr">xmlns:xsi</span>=<span class="string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">modelVersion</span>&gt;</span>4.0.0<span class="tag">&lt;/<span class="name">modelVersion</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>cn.itcast.service<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>itcast-service-provider<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>0.0.1-SNAPSHOT<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">packaging</span>&gt;</span>jar<span class="tag">&lt;/<span class="name">packaging</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">name</span>&gt;</span>itcast-service-provider<span class="tag">&lt;/<span class="name">name</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">description</span>&gt;</span>Demo project for Spring Boot<span class="tag">&lt;/<span class="name">description</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">parent</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-parent<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.0.6.RELEASE<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">relativePath</span>/&gt;</span> <span class="comment">&lt;!-- lookup parent from repository --&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">parent</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">properties</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">project.build.sourceEncoding</span>&gt;</span>UTF-8<span class="tag">&lt;/<span class="name">project.build.sourceEncoding</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">project.reporting.outputEncoding</span>&gt;</span>UTF-8<span class="tag">&lt;/<span class="name">project.reporting.outputEncoding</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">java.version</span>&gt;</span>1.8<span class="tag">&lt;/<span class="name">java.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">properties</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-jdbc<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-web<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.mybatis.spring.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>mybatis-spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.3.2<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>mysql<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>mysql-connector-java<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">scope</span>&gt;</span>runtime<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-test<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">scope</span>&gt;</span>test<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>tk.mybatis<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>mapper-spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.0.4<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.cloud<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-cloud-starter-netflix-eureka-client<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">build</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">plugins</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">plugin</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-maven-plugin<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">plugin</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">plugins</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">build</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependencyManagement</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.cloud<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-cloud-dependencies<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">version</span>&gt;</span>Finchley.SR1<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">type</span>&gt;</span>pom<span class="tag">&lt;/<span class="name">type</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">scope</span>&gt;</span>import<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependencyManagement</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">project</span>&gt;</span></span><br></pre></td></tr></table></figure><h4 id="5-3-2-2-application-yml"><a href="#5-3-2-2-application-yml" class="headerlink" title="5.3.2.2.application.yml"></a>5.3.2.2.application.yml</h4><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">server:</span></span><br><span class="line">  <span class="attr">port:</span> <span class="number">8081</span></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">datasource:</span></span><br><span class="line">    <span class="attr">url:</span> <span class="string">jdbc:mysql://localhost:3306/heima</span></span><br><span class="line">    <span class="attr">username:</span> <span class="string">root</span></span><br><span class="line">    <span class="attr">password:</span> <span class="string">root</span></span><br><span class="line">    <span class="attr">driverClassName:</span> <span class="string">com.mysql.jdbc.Driver</span></span><br><span class="line">  <span class="attr">application:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">service-provider</span> <span class="comment"># 应用名称，注册到eureka后的服务名称</span></span><br><span class="line"><span class="attr">mybatis:</span></span><br><span class="line">  <span class="attr">type-aliases-package:</span> <span class="string">cn.itcast.service.pojo</span></span><br><span class="line"><span class="attr">eureka:</span></span><br><span class="line">  <span class="attr">client:</span></span><br><span class="line">    <span class="attr">service-url:</span> <span class="comment"># EurekaServer地址</span></span><br><span class="line">      <span class="attr">defaultZone:</span> <span class="string">http://127.0.0.1:10086/eureka</span></span><br></pre></td></tr></table></figure><p>注意：</p><ul><li>这里我们添加了spring.application.name属性来指定应用名称，将来会作为应用的id使用。</li></ul><h4 id="5-3-2-3-引导类"><a href="#5-3-2-3-引导类" class="headerlink" title="5.3.2.3.引导类"></a>5.3.2.3.引导类</h4><p>在引导类上开启Eureka客户端功能</p><p>通过添加<code>@EnableDiscoveryClient</code>来开启Eureka客户端功能</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="meta">@EnableDiscoveryClient</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ItcastServiceProviderApplication</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        SpringApplication.run(ItcastServiceApplication.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>重启项目，访问<a href="http://127.0.0.1:10086/">Eureka监控页面</a>查看</p></blockquote><p><img src="/assets/1545178177947.png" alt="1545178177947"></p><p>我们发现service-provider服务已经注册成功了</p><h3 id="5-3-3-从Eureka获取服务"><a href="#5-3-3-从Eureka获取服务" class="headerlink" title="5.3.3.从Eureka获取服务"></a>5.3.3.从Eureka获取服务</h3><p>接下来我们修改itcast-service-consumer，尝试从EurekaServer获取服务。</p><p>方法与消费者类似，只需要在项目中添加EurekaClient依赖，就可以通过服务名称来获取信息了！</p><ol><li>pom.xml</li></ol><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">project</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://maven.apache.org/POM/4.0.0&quot;</span> <span class="attr">xmlns:xsi</span>=<span class="string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">modelVersion</span>&gt;</span>4.0.0<span class="tag">&lt;/<span class="name">modelVersion</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>cn.itcast.service<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>itcast-service-consumer<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>0.0.1-SNAPSHOT<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">packaging</span>&gt;</span>jar<span class="tag">&lt;/<span class="name">packaging</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">name</span>&gt;</span>itcast-service-consumer<span class="tag">&lt;/<span class="name">name</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">description</span>&gt;</span>Demo project for Spring Boot<span class="tag">&lt;/<span class="name">description</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">parent</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-parent<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.0.6.RELEASE<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">relativePath</span>/&gt;</span> <span class="comment">&lt;!-- lookup parent from repository --&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">parent</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">properties</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">project.build.sourceEncoding</span>&gt;</span>UTF-8<span class="tag">&lt;/<span class="name">project.build.sourceEncoding</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">project.reporting.outputEncoding</span>&gt;</span>UTF-8<span class="tag">&lt;/<span class="name">project.reporting.outputEncoding</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">java.version</span>&gt;</span>1.8<span class="tag">&lt;/<span class="name">java.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">properties</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-web<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-test<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">scope</span>&gt;</span>test<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- Eureka客户端 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.cloud<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-cloud-starter-netflix-eureka-client<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">build</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">plugins</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">plugin</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-maven-plugin<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">plugin</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">plugins</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">build</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- SpringCloud的依赖 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependencyManagement</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.cloud<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-cloud-dependencies<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">version</span>&gt;</span>Finchley.SR2<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">type</span>&gt;</span>pom<span class="tag">&lt;/<span class="name">type</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">scope</span>&gt;</span>import<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependencyManagement</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">project</span>&gt;</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><ol start="2"><li>修改配置</li></ol><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">server:</span></span><br><span class="line">  <span class="attr">port:</span> <span class="number">80</span></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">application:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">service-consumer</span></span><br><span class="line"><span class="attr">eureka:</span></span><br><span class="line">  <span class="attr">client:</span></span><br><span class="line">    <span class="attr">service-url:</span></span><br><span class="line">      <span class="attr">defaultZone:</span> <span class="string">http://localhost:10086/eureka</span></span><br></pre></td></tr></table></figure><ol start="3"><li>在启动类开启Eureka客户端</li></ol><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="meta">@EnableDiscoveryClient</span> <span class="comment">// 开启Eureka客户端</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ItcastServiceConsumerApplication</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> RestTemplate <span class="title function_">restTemplate</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">RestTemplate</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        SpringApplication.run(ItcastServiceConsumerApplication.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="4"><li>修改UserController代码，用DiscoveryClient类的方法，根据服务名称，获取服务实例：</li></ol><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Controller</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;consumer/user&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserController</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> RestTemplate restTemplate;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> DiscoveryClient discoveryClient; <span class="comment">// eureka客户端，可以获取到eureka中服务的信息</span></span><br><span class="line"></span><br><span class="line">    <span class="meta">@GetMapping</span></span><br><span class="line">    <span class="meta">@ResponseBody</span></span><br><span class="line">    <span class="keyword">public</span> User <span class="title function_">queryUserById</span><span class="params">(<span class="meta">@RequestParam(&quot;id&quot;)</span> Long id)</span>&#123;</span><br><span class="line">        <span class="comment">// 根据服务名称，获取服务实例。有可能是集群，所以是service实例集合</span></span><br><span class="line">        List&lt;ServiceInstance&gt; instances = discoveryClient.getInstances(<span class="string">&quot;service-provider&quot;</span>);</span><br><span class="line">        <span class="comment">// 因为只有一个Service-provider。所以获取第一个实例</span></span><br><span class="line">        <span class="type">ServiceInstance</span> <span class="variable">instance</span> <span class="operator">=</span> instances.get(<span class="number">0</span>);</span><br><span class="line">        <span class="comment">// 获取ip和端口信息，拼接成服务地址</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">baseUrl</span> <span class="operator">=</span> <span class="string">&quot;http://&quot;</span> + instance.getHost() + <span class="string">&quot;:&quot;</span> + instance.getPort() + <span class="string">&quot;/user/&quot;</span> + id;</span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> <span class="built_in">this</span>.restTemplate.getForObject(baseUrl, User.class);</span><br><span class="line">        <span class="keyword">return</span> user;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>5）Debug跟踪运行：</p><p><img src="/assets/1528534110188.png" alt="1528534110188"></p><p>生成的URL：</p><p><img src="/assets/1528534148651.png" alt="1528534148651"></p><p>访问结果：</p><p><img src="/assets/1535674665806.png" alt="1535674665806"></p><h2 id="5-4-Eureka详解"><a href="#5-4-Eureka详解" class="headerlink" title="5.4.Eureka详解"></a>5.4.Eureka详解</h2><p>接下来我们详细讲解Eureka的原理及配置。</p><h3 id="5-4-1-基础架构"><a href="#5-4-1-基础架构" class="headerlink" title="5.4.1.基础架构"></a>5.4.1.基础架构</h3><p>Eureka架构中的三个核心角色：</p><ul><li><p>服务注册中心</p><p>Eureka的服务端应用，提供服务注册和发现功能，就是刚刚我们建立的itcast-eureka。</p></li><li><p>服务提供者</p><p>提供服务的应用，可以是SpringBoot应用，也可以是其它任意技术实现，只要对外提供的是Rest风格服务即可。本例中就是我们实现的itcast-service-provider。</p></li><li><p>服务消费者</p><p>消费应用从注册中心获取服务列表，从而得知每个服务方的信息，知道去哪里调用服务方。本例中就是我们实现的itcast-service-consumer。</p></li></ul><h3 id="5-4-2-高可用的Eureka-Server"><a href="#5-4-2-高可用的Eureka-Server" class="headerlink" title="5.4.2.高可用的Eureka Server"></a>5.4.2.高可用的Eureka Server</h3><p>Eureka Server即服务的注册中心，在刚才的案例中，我们只有一个EurekaServer，事实上EurekaServer也可以是一个集群，形成高可用的Eureka中心。</p><blockquote><p>服务同步</p></blockquote><p>多个Eureka Server之间也会互相注册为服务，当服务提供者注册到Eureka Server集群中的某个节点时，该节点会把服务的信息同步给集群中的每个节点，从而实现<strong>数据同步</strong>。因此，无论客户端访问到Eureka Server集群中的任意一个节点，都可以获取到完整的服务列表信息。</p><blockquote><p>动手搭建高可用的EurekaServer</p></blockquote><p>我们假设要运行两个EurekaServer的集群，端口分别为：10086和10087。只需要把itcast-eureka启动两次即可。</p><p>1）启动第一个eurekaServer，我们修改原来的EurekaServer配置：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">server:</span></span><br><span class="line">  <span class="attr">port:</span> <span class="number">10086</span> <span class="comment"># 端口</span></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">application:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">eureka-server</span> <span class="comment"># 应用名称，会在Eureka中显示</span></span><br><span class="line"><span class="attr">eureka:</span></span><br><span class="line">  <span class="attr">client:</span></span><br><span class="line">    <span class="attr">service-url:</span> <span class="comment"># 配置其他Eureka服务的地址，而不是自己，比如10087</span></span><br><span class="line">      <span class="attr">defaultZone:</span> <span class="string">http://127.0.0.1:10087/eureka</span></span><br></pre></td></tr></table></figure><p>所谓的高可用注册中心，其实就是把EurekaServer自己也作为一个服务进行注册，这样多个EurekaServer之间就能互相发现对方，从而形成集群。因此我们做了以下修改：</p><ul><li>把service-url的值改成了另外一台EurekaServer的地址，而不是自己</li></ul><p>启动报错，很正常。因为10087服务没有启动：</p><p><img src="/assets/1528691515859.png" alt="1528691515859"></p><p>2）启动第二个eurekaServer，再次修改itcast-eureka的配置：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">server:</span></span><br><span class="line">  <span class="attr">port:</span> <span class="number">10087</span> <span class="comment"># 端口</span></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">application:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">eureka-server</span> <span class="comment"># 应用名称，会在Eureka中显示</span></span><br><span class="line"><span class="attr">eureka:</span></span><br><span class="line">  <span class="attr">client:</span></span><br><span class="line">    <span class="attr">service-url:</span> <span class="comment"># 配置其他Eureka服务的地址，而不是自己，比如10087</span></span><br><span class="line">      <span class="attr">defaultZone:</span> <span class="string">http://127.0.0.1:10086/eureka</span></span><br></pre></td></tr></table></figure><p>注意：idea中一个应用不能启动两次，我们需要重新配置一个启动器：</p><p><img src="/assets/1528540668018.png" alt="1528540668018"></p><p><img src="/assets/1528691728618.png" alt="1528691728618"></p><p><img src="/assets/1528693484031.png" alt="1528693484031"></p><p>然后启动即可。</p><p>3）访问集群，测试：</p><p><img src="/assets/1528693648200.png" alt="1528693648200"></p><p>4）客户端注册服务到集群</p><p>因为EurekaServer不止一个，因此注册服务的时候，service-url参数需要变化：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">eureka:</span></span><br><span class="line">  <span class="attr">client:</span></span><br><span class="line">    <span class="attr">service-url:</span> <span class="comment"># EurekaServer地址,多个地址以&#x27;,&#x27;隔开</span></span><br><span class="line">      <span class="attr">defaultZone:</span> <span class="string">http://127.0.0.1:10086/eureka,http://127.0.0.1:10087/eureka</span></span><br></pre></td></tr></table></figure><p>10086：</p><p><img src="/assets/1528693943950.png" alt="1528693943950"></p><p>10087：</p><p><img src="/assets/1528693979654.png" alt="1528693979654"></p><h3 id="5-4-3-服务提供者"><a href="#5-4-3-服务提供者" class="headerlink" title="5.4.3.服务提供者"></a>5.4.3.服务提供者</h3><p>服务提供者要向EurekaServer注册服务，并且完成服务续约等工作。</p><blockquote><p>服务注册</p></blockquote><p>服务提供者在启动时，会检测配置属性中的：<code>eureka.client.register-with-eureka=true</code>参数是否正确，事实上默认就是true。如果值确实为true，则会向EurekaServer发起一个Rest请求，并携带自己的元数据信息，Eureka Server会把这些信息保存到一个双层Map结构中。</p><ul><li>第一层Map的Key就是服务id，一般是配置中的<code>spring.application.name</code>属性</li><li>第二层Map的key是服务的实例id。一般host+ serviceId + port，例如：<code>locahost:service-provider:8081</code></li><li>值则是服务的实例对象，也就是说一个服务，可以同时启动多个不同实例，形成集群。</li></ul><blockquote><p>服务续约</p></blockquote><p>在注册服务完成以后，服务提供者会维持一个心跳（定时向EurekaServer发起Rest请求），告诉EurekaServer：“我还活着”。这个我们称为服务的续约（renew）；</p><p>有两个重要参数可以修改服务续约的行为：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">eureka:</span></span><br><span class="line">  <span class="attr">instance:</span></span><br><span class="line">    <span class="attr">lease-expiration-duration-in-seconds:</span> <span class="number">90</span></span><br><span class="line">    <span class="attr">lease-renewal-interval-in-seconds:</span> <span class="number">30</span></span><br></pre></td></tr></table></figure><ul><li>lease-renewal-interval-in-seconds：服务续约(renew)的间隔，默认为30秒</li><li>lease-expiration-duration-in-seconds：服务失效时间，默认值90秒</li></ul><p>也就是说，默认情况下每个30秒服务会向注册中心发送一次心跳，证明自己还活着。如果超过90秒没有发送心跳，EurekaServer就会认为该服务宕机，会从服务列表中移除，这两个值在生产环境不要修改，默认即可。</p><p>但是在开发时，这个值有点太长了，经常我们关掉一个服务，会发现Eureka依然认为服务在活着。所以我们在开发阶段可以适当调小。</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">eureka:</span></span><br><span class="line">  <span class="attr">instance:</span></span><br><span class="line">    <span class="attr">lease-expiration-duration-in-seconds:</span> <span class="number">10</span> <span class="comment"># 10秒即过期</span></span><br><span class="line">    <span class="attr">lease-renewal-interval-in-seconds:</span> <span class="number">5</span> <span class="comment"># 5秒一次心跳</span></span><br></pre></td></tr></table></figure><h3 id="5-4-4-服务消费者"><a href="#5-4-4-服务消费者" class="headerlink" title="5.4.4.服务消费者"></a>5.4.4.服务消费者</h3><blockquote><p>获取服务列表</p></blockquote><p>当服务消费者启动时，会检测<code>eureka.client.fetch-registry=true</code>参数的值，如果为true，则会拉取Eureka Server服务的列表只读备份，然后缓存在本地。并且<code>每隔30秒</code>会重新获取并更新数据。我们可以通过下面的参数来修改：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">eureka:</span></span><br><span class="line">  <span class="attr">client:</span></span><br><span class="line">    <span class="attr">registry-fetch-interval-seconds:</span> <span class="number">5</span></span><br></pre></td></tr></table></figure><p>生产环境中，我们不需要修改这个值。</p><p>但是为了开发环境下，能够快速得到服务的最新状态，我们可以将其设置小一点。</p><h3 id="5-4-5-失效剔除和自我保护"><a href="#5-4-5-失效剔除和自我保护" class="headerlink" title="5.4.5.失效剔除和自我保护"></a>5.4.5.失效剔除和自我保护</h3><blockquote><p>服务下线</p></blockquote><p>当服务进行正常关闭操作时，它会触发一个服务下线的REST请求给Eureka Server，告诉服务注册中心：“我要下线了”。服务中心接受到请求之后，将该服务置为下线状态。</p><blockquote><p>失效剔除</p></blockquote><p>有些时候，我们的服务提供方并不一定会正常下线，可能因为内存溢出、网络故障等原因导致服务无法正常工作。Eureka Server需要将这样的服务剔除出服务列表。因此它会开启一个定时任务，每隔60秒对所有失效的服务（超过90秒未响应）进行剔除。</p><p>可以通过<code>eureka.server.eviction-interval-timer-in-ms</code>参数对其进行修改，单位是毫秒，生产环境不要修改。</p><p>这个会对我们开发带来极大的不变，你对服务重启，隔了60秒Eureka才反应过来。开发阶段可以适当调整，比如：10秒</p><p><img src="/assets/1528696142799.png" alt="1528696142799"></p><blockquote><p>自我保护</p></blockquote><p>我们关停一个服务，就会在Eureka面板看到一条警告：</p><p><img src="/assets/1525618396076.png" alt="1525618396076"></p><p>这是触发了Eureka的自我保护机制。当一个服务未按时进行心跳续约时，Eureka会统计最近15分钟心跳失败的服务实例的比例是否超过了85%。在生产环境下，因为网络延迟等原因，心跳失败实例的比例很有可能超标，但是此时就把服务剔除列表并不妥当，因为服务可能没有宕机。Eureka就会把当前实例的注册信息保护起来，不予剔除。生产环境下这很有效，保证了大多数服务依然可用。</p><p>但是这给我们的开发带来了麻烦， 因此开发阶段我们都会关闭自我保护模式：（itcast-eureka）</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">eureka:</span></span><br><span class="line">  <span class="attr">server:</span></span><br><span class="line">    <span class="attr">enable-self-preservation:</span> <span class="literal">false</span> <span class="comment"># 关闭自我保护模式（缺省为打开）</span></span><br><span class="line">    <span class="attr">eviction-interval-timer-in-ms:</span> <span class="number">1000</span> <span class="comment"># 扫描失效服务的间隔时间（缺省为60*1000ms）</span></span><br></pre></td></tr></table></figure><h1 id="6-负载均衡Ribbon"><a href="#6-负载均衡Ribbon" class="headerlink" title="6.负载均衡Ribbon"></a>6.负载均衡Ribbon</h1><p>在刚才的案例中，我们启动了一个itcast-service-provider，然后通过DiscoveryClient来获取服务实例信息，然后获取ip和端口来访问。</p><p>但是实际环境中，我们往往会开启很多个itcast-service-provider的集群。此时我们获取的服务列表中就会有多个，到底该访问哪一个呢？</p><p>一般这种情况下我们就需要编写负载均衡算法，在多个实例列表中进行选择。</p><p>不过Eureka中已经帮我们集成了负载均衡组件：Ribbon，简单修改代码即可使用。</p><p>什么是Ribbon：</p><p><img src="/assets/1525619257397.png" alt="1525619257397"></p><p>接下来，我们就来使用Ribbon实现负载均衡。</p><h2 id="6-1-启动两个服务实例"><a href="#6-1-启动两个服务实例" class="headerlink" title="6.1.启动两个服务实例"></a>6.1.启动两个服务实例</h2><p>首先参照itcast-eureka启动两个ItcastServiceProviderApplication实例，一个8081，一个8082。</p><p><img src="/assets/1540644966386.png" alt="1540644966386"></p><p>Eureka监控面板：</p><p><img src="/assets/1540645032363.png" alt="1540645032363"></p><h2 id="6-2-开启负载均衡"><a href="#6-2-开启负载均衡" class="headerlink" title="6.2.开启负载均衡"></a>6.2.开启负载均衡</h2><p>因为Eureka中已经集成了Ribbon，所以我们无需引入新的依赖，直接修改代码。</p><p>修改itcast-service-consumer的引导类，在RestTemplate的配置方法上添加<code>@LoadBalanced</code>注解：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="meta">@LoadBalanced</span></span><br><span class="line"><span class="keyword">public</span> RestTemplate <span class="title function_">restTemplate</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">RestTemplate</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>修改调用方式，不再手动获取ip和端口，而是直接通过服务名称调用：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Controller</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;consumer/user&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserController</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> RestTemplate restTemplate;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//@Autowired</span></span><br><span class="line">    <span class="comment">//private DiscoveryClient discoveryClient; // 注入discoveryClient，通过该客户端获取服务列表</span></span><br><span class="line"></span><br><span class="line">    <span class="meta">@GetMapping</span></span><br><span class="line">    <span class="meta">@ResponseBody</span></span><br><span class="line">    <span class="keyword">public</span> User <span class="title function_">queryUserById</span><span class="params">(<span class="meta">@RequestParam(&quot;id&quot;)</span> Long id)</span>&#123;</span><br><span class="line">        <span class="comment">// 通过client获取服务提供方的服务列表，这里我们只有一个</span></span><br><span class="line">        <span class="comment">// ServiceInstance instance = discoveryClient.getInstances(&quot;service-provider&quot;).get(0);</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">baseUrl</span> <span class="operator">=</span> <span class="string">&quot;http://service-provider/user/&quot;</span> + id;</span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> <span class="built_in">this</span>.restTemplate.getForObject(baseUrl, User.class);</span><br><span class="line">        <span class="keyword">return</span> user;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>访问页面，查看结果：</p><p><img src="/assets/1535674665806.png" alt="1535674665806"></p><p>完美！</p><h2 id="6-3-源码跟踪"><a href="#6-3-源码跟踪" class="headerlink" title="6.3.源码跟踪"></a>6.3.源码跟踪</h2><p>为什么我们只输入了service名称就可以访问了呢？之前还要获取ip和端口。</p><p>显然有人帮我们根据service名称，获取到了服务实例的ip和端口。它就是<code>LoadBalancerInterceptor</code></p><p>在如下代码打断点：</p><p><img src="/assets/1528774637934.png" alt="1528774637934"></p><p>一路源码跟踪：RestTemplate.getForObject –&gt; RestTemplate.execute –&gt; RestTemplate.doExecute：</p><p><img src="/assets/1528776129378.png" alt="1528776129378"></p><p>点击进入AbstractClientHttpRequest.execute –&gt; AbstractBufferingClientHttpRequest.executeInternal –&gt; InterceptingClientHttpRequest.executeInternal –&gt; InterceptingClientHttpRequest.execute:</p><p><img src="/assets/1528776489965.png" alt="1528776489965"></p><p>继续跟入：LoadBalancerInterceptor.intercept方法</p><p><img src="/assets/1528775270103.png" alt="1528775270103"></p><p>继续跟入execute方法：发现获取了8082端口的服务</p><p><img src="/assets/1528775890956.png" alt="1528775890956"></p><p>再跟下一次，发现获取的是8081：</p><p><img src="/assets/1528775845812.png" alt="1528775845812"></p><h2 id="6-4-负载均衡策略"><a href="#6-4-负载均衡策略" class="headerlink" title="6.4.负载均衡策略"></a>6.4.负载均衡策略</h2><p>Ribbon默认的负载均衡策略是简单的轮询，我们可以测试一下：</p><p>编写测试类，在刚才的源码中我们看到拦截中是使用RibbonLoadBalanceClient来进行负载均衡的，其中有一个choose方法，找到choose方法的接口方法，是这样介绍的：</p><p><img src="/assets/1525622320277.png" alt="1525622320277"></p><p>现在这个就是负载均衡获取实例的方法。</p><p>我们注入这个类的对象，然后对其测试：</p><p><img src="/assets/1528780835917.png" alt="1528780835917"></p><p>测试内容：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@RunWith(SpringRunner.class)</span></span><br><span class="line"><span class="meta">@SpringBootTest(classes = ItcastServiceConsumerApplication.class)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">LoadBalanceTest</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> RibbonLoadBalancerClient client;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testLoadBalance</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">100</span>; i++) &#123;</span><br><span class="line">            <span class="type">ServiceInstance</span> <span class="variable">instance</span> <span class="operator">=</span> <span class="built_in">this</span>.client.choose(<span class="string">&quot;service-provider&quot;</span>);</span><br><span class="line">            System.out.println(instance.getHost() + <span class="string">&quot;:&quot;</span> +instance.getPort());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>结果：</p><p><img src="/assets/1535338345659.png" alt="1535338345659"></p><p>符合了我们的预期推测，确实是轮询方式。</p><p>我们是否可以修改负载均衡的策略呢？</p><p>继续跟踪源码，发现这么一段代码：</p><p><img src="/assets/1525622652849.png" alt="1525622652849"></p><p>我们看看这个rule是谁：</p><p><img src="/assets/1525622699666.png" alt="1525622699666"></p><p>这里的rule默认值是一个<code>RoundRobinRule</code>，看类的介绍：</p><p><img src="/assets/1525622754316.png" alt="1525622754316"></p><p>这不就是轮询的意思嘛。</p><p>我们注意到，这个类其实是实现了接口IRule的，查看一下：</p><p><img src="/assets/1525622817451.png" alt="1525622817451"></p><p>定义负载均衡的规则接口。</p><p>它有以下实现：</p><p><img src="/assets/1528782624098.png" alt="1528782624098"></p><p>SpringBoot也帮我们提供了修改负载均衡规则的配置入口，在itcast-service-consumer的application.yml中添加如下配置：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">server:</span></span><br><span class="line">  <span class="attr">port:</span> <span class="number">80</span></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">application:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">service-consumer</span></span><br><span class="line"><span class="attr">eureka:</span></span><br><span class="line">  <span class="attr">client:</span></span><br><span class="line">    <span class="attr">service-url:</span></span><br><span class="line">      <span class="attr">defaultZone:</span> <span class="string">http://127.0.0.1:10086/eureka</span></span><br><span class="line"><span class="attr">service-provider:</span></span><br><span class="line">  <span class="attr">ribbon:</span></span><br><span class="line">    <span class="attr">NFLoadBalancerRuleClassName:</span> <span class="string">com.netflix.loadbalancer.RandomRule</span></span><br></pre></td></tr></table></figure><p>格式是：<code>&#123;服务名称&#125;.ribbon.NFLoadBalancerRuleClassName</code>，值就是IRule的实现类。</p><p>再次测试，发现结果变成了随机：</p><p><img src="/assets/1528782514987.png" alt="1528782514987"></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;0-学习目标&quot;&gt;&lt;a href=&quot;#0-学习目标&quot; class=&quot;headerlink&quot; title=&quot;0.学习目标&quot;&gt;&lt;/a&gt;0.学习目标&lt;/h1&gt;&lt;ul&gt;
&lt;li&gt;了解系统架构的演变&lt;/li&gt;
&lt;li&gt;了解RPC与Http的区别&lt;/li&gt;
&lt;li&gt;知道什么是S</summary>
      
    
    
    
    <category term="java" scheme="http://example.com/categories/java/"/>
    
    
  </entry>
  
  <entry>
    <title>视频开发中的一些基础概念</title>
    <link href="http://example.com/2022/01/16/%E8%A7%86%E9%A2%91%E5%BC%80%E5%8F%91%E4%B8%AD%E7%9A%84%E4%B8%80%E4%BA%9B%E5%9F%BA%E7%A1%80%E6%A6%82%E5%BF%B5/"/>
    <id>http://example.com/2022/01/16/%E8%A7%86%E9%A2%91%E5%BC%80%E5%8F%91%E4%B8%AD%E7%9A%84%E4%B8%80%E4%BA%9B%E5%9F%BA%E7%A1%80%E6%A6%82%E5%BF%B5/</id>
    <published>2022-01-16T07:17:49.000Z</published>
    <updated>2025-09-15T07:26:55.177Z</updated>
    
    <content type="html"><![CDATA[<p>在进行视频开发之前，如果你对颜色编码格式不是很熟悉，建议先阅读一下这篇文章：<a href="https://blog.csdn.net/junzia/article/details/76315120">yuv和rgb的相关知识</a>。有一个大概了解之后，在看下面的视频相关的基础知识。</p><p>封装格式的辨认很简单，大多数情况下，拓展名就是封装格式的名字。比如一个文件“金刚狼.avi”，那它就是属于avi的封装格式，“钢之炼金术师.Mkv”，那它就属于mkv封装格式， “喜羊羊与灰太狼.rmvb”就是rmvb封装格式。简而言之，视频文件后缀名就是封装格式的名字。不过wmv和rmvb就比较特殊，后面编码器部分再说。封装格式就是酒瓶。酒瓶就不影响酒的口感，同样，封装格式一样不影响视频画质。它只负责把内部的视频轨和音频轨集成在一起，只起到一个文件夹（或者压缩包）的作用，并没有对视频轨和音频轨造成影响。</p><h4 id="几种主流媒体格式"><a href="#几种主流媒体格式" class="headerlink" title="几种主流媒体格式"></a>几种主流媒体格式</h4><ol><li><strong>AVI(Audio Video Interactive)</strong> 比较早的AVI是Microsoft开发的。其含义是Audio Video Interactive，就是把视频和音频编码混合在一起存储。<br>AVI也是最长寿的格式，已存在10余年了，虽然发布过改版（V2.0于1996年发布），但已显老态。AVI格式上限制比较多，只能有一个视频轨道和一个音频轨道（现在有非标准插件可加入最多两个音频轨道），还可以有一些附加轨道，如文字等。AVI格式不提供任何控制功能。扩展名：avi<br>总结：只能封装一条视频和一条音频，不能封装字幕，没有流媒体功能（就是不能使用它进行在线播放的意思）</li><li>**WMV（Windows Media Video）**是微软公司开发的一组数字视频编解码格式的通称，ASF（Advanced Systems Format）是其封装格式。 ASF封装的WMV档具有“数字版权保护”功能。扩展名:wmv&#x2F;asf、wmvhd 其音频编码采用WMA，视频编码采用WMV,需要版权支持且机顶盒支持的不多。</li><li>**MPEG（Moving Picture Experts Group）**是一个国际标准化组织（ISO）认可的媒体封装形式，受到大部份机器的支持。其存储方式多样，可以适应不同的应用环境。MPEG-4档的档容器格式在Part 1（mux）、14（asp）、15（avc）等中规定。MPEG的控制功能丰富，可以有多个视频（即角度）、音轨、字幕（位图字幕）等等。<ol><li>MPEG1是最早出现的，VCD这个具体的格式最早是从日本来的并遵从MPEG1规格</li><li>MPEG2：其代表是DVD。一般为480P（640*480）。DVD当时都符合这个标准。Xvid 和H.264同属于MPEG4，MPEG-2还可能称作MPEG2-HD，MPEG2-TS，其实在ITU-T的命名规范中被称之为H.262</li><li>H.264有两个名称，一个是沿用ITU-T组织的H.26x名称，叫“H.264”，另一个则是“MPEG-4AVC（MPEG4第10部分）”，AVC意为高级视频编码。</li><li>H264的数据压缩率比当前DVD系统中使用的MPEG-2高2-3倍，比MPEG-4高1.5-2倍。x264编码，是H.264编码的一个开源分支，它符合H.264标准，其功能在于编码(encoding)，而不作为解码器(decoder)使用，264编码最大的特点在于注重实用，它在不明显降低编码性能的前提下，努力降低编码的计算复杂度，X264对于H.264编码中的一些复杂编码特性做了折衷处理，其压缩视频体积小于Xvid(MPEG4)编码视频</li><li>mp4格式是H.264编码指定使用的标准封装格式，3GP是MP4格式的一种简化版本，减少了储存空间和较低的频宽需求，让手机上有限的储存空间可以使用可以封装多种多条编码的视频和音频，也可以封装字幕，但没有流媒体功能，可以做成分片式进行流式播放。</li></ol></li><li><strong>Matroska</strong> 是一种新的多媒体封装格式，这个封装格式可把多种不同编码的视频及16条或以上不同格式的音频和语言不同的字幕封装到一个Matroska Media档内。它也是其中一种开放源代码的多媒体封装格式。Matroska同时还可以提供非常好的交互功能，而且比MPEG的方便、强大。扩展名:mkv</li><li>**Real Video或者称Real Media（RM）**档是由RealNetworks开发的一种档容器。它通常只能容纳Real Video和Real Audio编码的媒体。该档带有一定的交互功能，允许编写脚本以控制播放。RM，尤其是可变比特率的RMVB格式，体积很小，非常受到网络下载者的欢迎。扩展名：rm&#x2F;rmvb</li><li><strong>QuickTime File Format</strong> 是由苹果公司开发的容器。1998年2月11日，国际标准组织（ISO）认可QuickTime文件格式作为MPEG-4标准的基础。QT可存储的内容相当丰富，除了视频、音频以外还可支持图片、文字（文本字幕）等。扩展名：mov、qt</li><li><strong>Ogg Media</strong> 是一个完全开放性的多媒体系统计划，OGM（Ogg Media File）是其容器格式。OGM可以支持多视频、音频、字幕（文本字幕）等多种轨道。扩展名：ogg</li><li><strong>MOD</strong> 格式是JVC生产的硬盘摄录机所采用的存储格式名称</li><li><strong>MKV</strong> 和mp4差不多，但有流媒体功能，可以使用它进行在线播放，还可以使用它封装rv（rm&#x2F;rmvb）编码</li></ol><p>另外：<br>视频封装格式是MP4，MKV，或者其它，它们都是：视频流，音频流，字幕完全用自己独立的轨道，并且可以是外挂字幕。<br>字幕后缀名为str，ass，ssa等。这些字幕有自己的特效，什么时候以怎样的形式出现哪些文字，都是由写好的代码控制的。</p><h4 id="编码格式"><a href="#编码格式" class="headerlink" title="编码格式"></a>编码格式</h4><p>就是指通过特定的压缩技术，将某个视频格式的文件转换成另 一种视频格式文件的方式。目前视频流传输中最为重要的编解码标准有国际电联的H.264，运动静止图像专家组的M-JPEG和国际标准化组织运动图像专家 组的MPEG系列标准，此外在互联网上被广泛应用的还有Real-Networks的RealVideo、微软公司的WMV以及Apple公司的 QuickTime等。</p><p>在这里我们主要讲一下主流的H.264编码。H.264 使图像压缩技术上升到了一个更高的阶段，能够在较低带宽上提供高质量的图像传输，该优点非常适合国内运营商用户量大、接入网&#x2F;骨干网带宽相对有限的状况。 在同等的画质下，H.264 比上一代编码标准MPEG2 平均节约64％的传输码流，而比MPEG4 ASP 要平均节约39％的传输码流。如果要具体讲解H.264相关的知识，又是一大篇，而且博主知识有限，不能很好的说清楚，所以这里就只能贴上一篇参考文章了：<a href="https://www.cnblogs.com/lidabo/p/6857616.html">如何实现1080P延迟低于500ms的实时超清直播传输技术</a></p><h4 id="视频码率"><a href="#视频码率" class="headerlink" title="视频码率"></a>视频码率</h4><p>视频码率是指视频文件在单位时间内使用的数据流量，也叫码流率。码率越大，说明单位时间内取样率越大，数据流精度就越高，这样表现出来的的效果就是：视频画面更清晰画质更高。这里要和视频帧率区分开。</p><h4 id="视频帧率"><a href="#视频帧率" class="headerlink" title="视频帧率"></a>视频帧率</h4><p>通常说一个视频的25帧，指的就是这个视频帧率，即1秒中会显示25帧；视频帧率影响的是画面流畅感，也就是说视频帧率超高，表现出来的效果就是：画面越显得流畅。你也可以这样理解，假设1秒只显1帧，那么一段视频看起来，就是有很明显的卡顿感，不流畅不连惯。当然视频帧率越高，意味着画面越多，也就相应的，这个视频文件的大小也会随之增加，占用存储空间也就增大了。</p><h4 id="后话"><a href="#后话" class="headerlink" title="后话"></a>后话</h4><p>封装格式和编码的关系，就是和酒与酒瓶的关系差不多，而播放器就是开酒器。为了可以喝更好的酒，最好先了解酒是什么酒，酒瓶是什么酒瓶，怎么使用开酒器开酒瓶。</p><p>码率或者可以比喻做酒的原料。对于同一个酒瓶和做同一种酒的情况下，如果原料太少，又要要求用酒把酒瓶灌满，此时只好兑水了，酒的品质就会变差了。然而，如果原料太多，又会造成原料浪费。所以要做好酒，我们就需要充足的原料。同样道理，压制一个视频，充足的码率是非常需要的。如果码率过高，画质提升不上去的同时还使视频体积过大。如果码率不足，视频容易出各种各样的问题，导致压崩。当然，码率不是画质的唯一指标，压制的时候的各种参数设置，各种滤镜也是非常重要的。如果码率给得足够高，理论上各编码的画质是一样的，不过体积完全不一样。</p><p>本篇文章只做一个大概介绍，因为每一个点往深入讲都能讲很长时间，视频开发中的每一个门类，都可以深挖，衍生出一个又一个充满技术挑战的话题。音视频的路很长，更多的还是要靠自己去学习和实践，希望我的文章能对你产生一点点意义。</p><p>参考链接：<a href="https://blog.csdn.net/andyhuabing/article/details/8063298">封装格式和编码格式</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;在进行视频开发之前，如果你对颜色编码格式不是很熟悉，建议先阅读一下这篇文章：&lt;a href=&quot;https://blog.csdn.net/junzia/article/details/76315120&quot;&gt;yuv和rgb的相关知识&lt;/a&gt;。有一个大概了解之后，在看下面的视频相</summary>
      
    
    
    
    <category term="java" scheme="http://example.com/categories/java/"/>
    
    
  </entry>
  
  <entry>
    <title>烂笔头-深入理解java虚拟机</title>
    <link href="http://example.com/2022/01/15/%E7%83%82%E7%AC%94%E5%A4%B4-%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3java%E8%99%9A%E6%8B%9F%E6%9C%BA/"/>
    <id>http://example.com/2022/01/15/%E7%83%82%E7%AC%94%E5%A4%B4-%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3java%E8%99%9A%E6%8B%9F%E6%9C%BA/</id>
    <published>2022-01-15T07:16:55.000Z</published>
    <updated>2025-09-15T07:26:55.176Z</updated>
    
    <content type="html"><![CDATA[<p>今天开始拜读《深入理解java虚拟机》，做个笔记，避免自己忘记。</p><p>java运行时数据区域分为：</p><ol><li>程序计数器：记录下一次需要执行的字节码指令，分支，循环，跳转，异常处理，线程恢复等基础功能都需要依赖他。java虚拟机的多线程是通过线程轮流切换分配处理器执行时间的方式实现的，在一个确切的时间点，一个处理器都只会执行一条线程中的指令，所以为了线程能够切换到正常的位置，每个线程都有独立的程序计数器，这类内存区域也叫线程私有的内存。</li><li>java虚拟机栈：也是线程私有的的。生命周期与线程相同。很多人把java内存分为堆内存和栈内存，java内存区域远比这个复杂，所指的栈就是这个，或者说是虚拟机栈中局部变量表的部分。局部变量表包括编译期可知的各种基本数据类型（boolean,byte,char,short,int,float,long,double），对象的引用，和返回值类型。如果线程请求的栈深度大于虚拟机设定的允许的深度，会抛出StackOverFlowError。如果虚拟机可以动态扩展，扩展时无法申请到足够的内存，就会抛出OutMemoryError。</li><li>本地方法栈：和上面不同的地方是虚拟机栈为java方法服务（也就是字节码）。本地方法栈为native方法服务，有的jvm是把这两者合二为一的，与虚拟机栈一样，本地方法栈也会抛出StackOverFlowError和OutMemoryError。</li><li>java堆：是所有线程共享的内存区域。唯一的目的就是存放对象实例，几乎所有的对象实例都是在这里分配内存。java堆是垃圾收集器管理的主要区域，所以很多时候也被叫做GC堆。需要注意的是，java堆可以使固定的，也可以是扩展的，当前很多流行的虚拟机都可以扩展（通过-Xmx和-Xms控制，终于知道当android studio卡顿时设置的那对参数了），如果在堆内存中没有内存完成实例分配，并且堆内存无法扩展，就会抛出OutMemoryError。</li><li>方法区：和java堆一样，是所有线程共享的内存区域。用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。这区域可以选择不实现垃圾收集。内存回收的主要目标是针对常量池的回收和对类型的卸载。方法区无法满足内存分配时，会抛出OutMemoryError。</li><li>运行常量池：方法区的一部分，用于存放编译期生成的各种字面量和符号引用，这部分内容将在类加载后进入方法区的运行时常量池中存放。</li><li>直接内存：并不是jvm运行时数据区的一部分，因为这部分内存被频繁的使用，也可能导致OutMemoryError，所以就拿出来讲，在jdk1.4时引入了nio(New Input?Output)类，它可以使用native函数库直接分配堆外内存，避免了在java堆和Native堆来回复制数据，显著提高性能。因为是直接内存，如果设置了虚拟机参数内存信息，各个内存之和+直接内存可能会大于实际的物理内存，就会造成OutMemoryError。</li></ol><p>贴上第二个会出现异常的代码：</p><p>堆栈溢出错误一般是递归调用，原则上循环嵌套次数本身是没有限制的，限制的是占用的栈空间，如果你的函数里定义了很多很多变量，栈空间就会用完得比较快。 层数达到了环境的设定值,就会抛异常。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">StackOverflowTest</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        method();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">method</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">for</span>(;;)</span><br><span class="line">            method();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>内存溢出：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OutOfMemoryTest</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span>&#123;</span><br><span class="line">        List list=<span class="keyword">new</span> <span class="title class_">ArrayList</span>();</span><br><span class="line">        <span class="keyword">for</span>(;;)&#123;</span><br><span class="line">            <span class="type">int</span>[] tmp=<span class="keyword">new</span> <span class="title class_">int</span>[<span class="number">1000000</span>];</span><br><span class="line">            list.add(tmp);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="对象的创建"><a href="#对象的创建" class="headerlink" title="对象的创建"></a>对象的创建</h4><ol><li>jvm遇到了一条new指令</li><li>检查这个指令的参数能否在常量池中定位到一个类的引用以及是否已经被加载、解析和初始化过(类加载检查)</li><li>为新生对象分配内存</li><li><init>方法执行，把对象按照程序猿的意愿进行初始化</li></ol><h4 id="对象的内存布局"><a href="#对象的内存布局" class="headerlink" title="对象的内存布局"></a>对象的内存布局</h4><p>对象在内存中存储的布局可以分为三块区域：</p><ol><li>对象头：又分为两个部分，第一个部分存放对象自身的运行时数据，比如哈希码、锁状态等，第二部分是类型指针，即对象指向它类的元数据的指针，虚拟机通过该指针确定这个对象是哪个类的实例。</li><li>实例数据：对象真正存储的有效信息，就是在程序中所定义的各种类型的字段内容。</li><li>对齐填充：不是必然存在的，也没有特别含义，只是占位的作用。有些虚拟机的对象大小必须为8的倍数，而对象头刚刚好是8的倍数，如果实例数据不是8的倍数，那就需要填充空白部分。</li></ol><h3 id="3-垃圾收集器与内存分配策略"><a href="#3-垃圾收集器与内存分配策略" class="headerlink" title="3.垃圾收集器与内存分配策略"></a>3.垃圾收集器与内存分配策略</h3><h4 id="对象存活算法"><a href="#对象存活算法" class="headerlink" title="对象存活算法"></a>对象存活算法</h4><ol><li>引用计数：给对象中添加一个引用计数器，当有一个地方引用它时，计数值就加1，引用失效时，计数值就减1，如果计数器的值为0则说明对象是不可能再被使用了。虽然效率很高，但是主流java虚拟机并没有使用它来管理内存，最主要的原因是它很难解决对象之间的相互循环引用问题。</li><li>可达性分析算法</li></ol>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;今天开始拜读《深入理解java虚拟机》，做个笔记，避免自己忘记。&lt;/p&gt;
&lt;p&gt;java运行时数据区域分为：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;程序计数器：记录下一次需要执行的字节码指令，分支，循环，跳转，异常处理，线程恢复等基础功能都需要依赖他。java虚拟机的多线程是通过线程轮</summary>
      
    
    
    
    <category term="java" scheme="http://example.com/categories/java/"/>
    
    
  </entry>
  
  <entry>
    <title>深入理解多线程之线程同步工具</title>
    <link href="http://example.com/2021/09/15/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E5%A4%9A%E7%BA%BF%E7%A8%8B%E4%B9%8B%E7%BA%BF%E7%A8%8B%E5%90%8C%E6%AD%A5%E5%B7%A5%E5%85%B7/"/>
    <id>http://example.com/2021/09/15/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E5%A4%9A%E7%BA%BF%E7%A8%8B%E4%B9%8B%E7%BA%BF%E7%A8%8B%E5%90%8C%E6%AD%A5%E5%B7%A5%E5%85%B7/</id>
    <published>2021-09-15T07:14:42.000Z</published>
    <updated>2025-09-15T07:26:55.179Z</updated>
    
    <content type="html"><![CDATA[<p>线程同步工具主要包括：</p><ul><li>闭锁(CountDownLatch)</li><li>栅栏(CyclicBarrier)</li><li>信号量(Semaphore)</li><li>阻塞队列(BlockingQueue)</li><li>FutureTask</li></ul><h3 id="CountDownLatch"><a href="#CountDownLatch" class="headerlink" title="CountDownLatch"></a>CountDownLatch</h3><p>可以让其他线程先执行，等执行完成之后再执行当前线程。<code>CountDownLatch</code>的构造方法传入了一个整数，相当于计数器，每次调用<code>CountDownLatch</code>的<code>countDown()</code>方法，计数器减一，而调用<code>await()</code>会阻塞当前线程，直到计数器为0后，才执行<code>await()</code>之后的代码。</p><h4 id="使用场景"><a href="#使用场景" class="headerlink" title="使用场景"></a>使用场景</h4><p>统计报表的优化，一般统计报表都需要从多个表中将数据统一展示出来，有时候遇上数据量比较大的情况，统计指标涉及到的业务范围也比较多，这个时候接口响应速度就会很慢，然后就可以使用<code>CountDownLatch</code>，将每个查询任务拆分到单独的线程中去执行，执行完成后调用<code>countDown()</code>方法，主线程调用<code>await()</code>等待所有任务完成之后进行汇总，这样使得原来串行化的任务变成了并行，效率自然提高上去了。</p><p>下面的代码初始化了一个线程数为10的线程池，并且将<code>CountDownLatch</code>的计数器设置为相同大小，当线程池中的任务执行完毕之后，主线程才开始往下执行：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CountDownLatchTest</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> <span class="number">10</span>;</span><br><span class="line">        <span class="type">CountDownLatch</span> <span class="variable">countDownLatch</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CountDownLatch</span>(count);</span><br><span class="line">        <span class="type">ExecutorService</span> <span class="variable">fixedThreadPool</span> <span class="operator">=</span> Executors.newFixedThreadPool(count);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; count; i++) &#123;</span><br><span class="line">            <span class="type">CountDownLatchRunnable</span> <span class="variable">runnable</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CountDownLatchRunnable</span>((i + <span class="number">1</span>) * <span class="number">1000</span>,countDownLatch);</span><br><span class="line">            fixedThreadPool.execute(runnable);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            countDownLatch.await();</span><br><span class="line">            System.out.println(<span class="string">&quot;所有runnable执行完毕，开始向下执行&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;<span class="keyword">finally</span> &#123;</span><br><span class="line">         fixedThreadPool.shutdown();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">CountDownLatchRunnable</span> <span class="keyword">implements</span> <span class="title class_">Runnable</span> &#123;</span><br><span class="line">        <span class="keyword">private</span> <span class="type">long</span> sleepTime;</span><br><span class="line">        <span class="keyword">private</span> CountDownLatch latch;</span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">CountDownLatchRunnable</span><span class="params">(<span class="type">long</span> sleepTime, CountDownLatch countDownLatch)</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.sleepTime = sleepTime;</span><br><span class="line">            <span class="built_in">this</span>.latch = countDownLatch;</span><br><span class="line">        &#125;</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_">run</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                System.out.println(Thread.currentThread().getName()+<span class="string">&quot;线程开始执行&quot;</span>);</span><br><span class="line">                TimeUnit.MILLISECONDS.sleep(sleepTime);</span><br><span class="line">                latch.countDown();</span><br><span class="line">                System.out.println(Thread.currentThread().getName()+<span class="string">&quot;线程执行完毕&quot;</span>);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>执行结果：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">pool-1-thread-1线程开始执行</span><br><span class="line">pool-1-thread-2线程开始执行</span><br><span class="line">pool-1-thread-3线程开始执行</span><br><span class="line">pool-1-thread-8线程开始执行</span><br><span class="line">pool-1-thread-4线程开始执行</span><br><span class="line">pool-1-thread-9线程开始执行</span><br><span class="line">pool-1-thread-5线程开始执行</span><br><span class="line">pool-1-thread-10线程开始执行</span><br><span class="line">pool-1-thread-6线程开始执行</span><br><span class="line">pool-1-thread-7线程开始执行</span><br><span class="line">pool-1-thread-1线程执行完毕</span><br><span class="line">pool-1-thread-2线程执行完毕</span><br><span class="line">pool-1-thread-3线程执行完毕</span><br><span class="line">pool-1-thread-4线程执行完毕</span><br><span class="line">pool-1-thread-5线程执行完毕</span><br><span class="line">pool-1-thread-6线程执行完毕</span><br><span class="line">pool-1-thread-7线程执行完毕</span><br><span class="line">pool-1-thread-8线程执行完毕</span><br><span class="line">pool-1-thread-9线程执行完毕</span><br><span class="line">pool-1-thread-10线程执行完毕</span><br><span class="line">所有runnable执行完毕，开始向下执行</span><br></pre></td></tr></table></figure><h3 id="CyclicBarrier"><a href="#CyclicBarrier" class="headerlink" title="CyclicBarrier"></a>CyclicBarrier</h3><p>等待其他线程同时到达（会阻塞当前线程），所有线程同时到达后，才继续往下执行。<code>CyclicBarrier</code>有两个构造方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="title function_">CyclicBarrier</span><span class="params">(<span class="type">int</span> parties)</span></span><br><span class="line"><span class="keyword">public</span> <span class="title function_">CyclicBarrier</span><span class="params">(<span class="type">int</span> parties, Runnable barrierAction)</span></span><br></pre></td></tr></table></figure><p>第一个参数是线程的数量，也就是说当有这么多个线程到达之后，所有线程才继续往下执行。第二个参数是一个<code>Runnable</code>，也就是说，所有线程到达之后，会执行这个<code>Runnable</code>，你可以在它的run方法里做一些处理。</p><h4 id="使用场景-1"><a href="#使用场景-1" class="headerlink" title="使用场景"></a>使用场景</h4><p>不难看出，<code>CyclicBarrier</code>可以实现和<code>CountDownLatch</code>一样的效果，不过<code>CyclicBarrier</code>有一个<code>reset</code>方法，可以重复利用，而<code>CountDownLatch</code>只能用一次。而且<code>CyclicBarrier</code>可以在做了一些操作之后各个线程继续往下执行。</p><p>比如说，你和几个朋友约好了，明天早上集合到你家，开车出去玩，第二天早上你得等你的朋友们都到齐上车了，才能开车出发。出发之前还可以一起吃个早饭，而<code>CountDownLatch</code>是做不到的。</p><p><img src="https://user-gold-cdn.xitu.io/2020/7/23/173796636c81aabe?w=325&h=222&f=gif&s=941628"></p><p>下面的代码初始化了一个线程数为10的线程池，将<code>CyclicBarrier</code>的线程数大小也设置为10，这样线程到达<code>cyclicBarrier.await()</code>的时候都会被阻塞，直到最后一个线程到达：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CyclicBarrierTest</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> <span class="number">10</span>;</span><br><span class="line">        <span class="type">ExecutorService</span> <span class="variable">fixedThreadPool</span> <span class="operator">=</span> Executors.newFixedThreadPool(count);</span><br><span class="line">        <span class="type">CyclicBarrier</span> <span class="variable">cyclicBarrier</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CyclicBarrier</span>(count, <span class="keyword">new</span> <span class="title class_">Runnable</span>() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;所有线程已经到达，可以在这里面做一些东西&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; count; i++) &#123;</span><br><span class="line">            <span class="type">CyclicBarrierRunnable</span> <span class="variable">runnable</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CyclicBarrierRunnable</span>((i + <span class="number">1</span>) * <span class="number">1000</span>,cyclicBarrier);</span><br><span class="line">            fixedThreadPool.execute(runnable);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">CyclicBarrierRunnable</span> <span class="keyword">implements</span> <span class="title class_">Runnable</span>&#123;</span><br><span class="line">        <span class="keyword">private</span> CyclicBarrier cyclicBarrier;</span><br><span class="line">        <span class="keyword">private</span> <span class="type">long</span> sleepTime;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">CyclicBarrierRunnable</span><span class="params">(<span class="type">long</span> sleepTime, CyclicBarrier cyclicBarrier)</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.cyclicBarrier = cyclicBarrier;</span><br><span class="line">            <span class="built_in">this</span>.sleepTime = sleepTime;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                System.out.println(Thread.currentThread().getName() + <span class="string">&quot; 线程开始执行任务&quot;</span>);</span><br><span class="line">                TimeUnit.MILLISECONDS.sleep(sleepTime);</span><br><span class="line">                System.out.println(Thread.currentThread().getName() + <span class="string">&quot; 到达珊阑处，等待其他线程到达&quot;</span>);</span><br><span class="line">                cyclicBarrier.await();</span><br><span class="line">                System.out.println(Thread.currentThread().getName() + <span class="string">&quot; 所有线程到达，开始向下执行&quot;</span>);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (BrokenBarrierException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;<span class="keyword">catch</span> (InterruptedException e)&#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>执行结果：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">pool-1-thread-2 线程开始执行任务</span><br><span class="line">pool-1-thread-3 线程开始执行任务</span><br><span class="line">pool-1-thread-4 线程开始执行任务</span><br><span class="line">pool-1-thread-1 线程开始执行任务</span><br><span class="line">pool-1-thread-6 线程开始执行任务</span><br><span class="line">pool-1-thread-5 线程开始执行任务</span><br><span class="line">pool-1-thread-8 线程开始执行任务</span><br><span class="line">pool-1-thread-9 线程开始执行任务</span><br><span class="line">pool-1-thread-7 线程开始执行任务</span><br><span class="line">pool-1-thread-10 线程开始执行任务</span><br><span class="line">pool-1-thread-1 到达珊阑处，等待其他线程到达</span><br><span class="line">pool-1-thread-2 到达珊阑处，等待其他线程到达</span><br><span class="line">pool-1-thread-3 到达珊阑处，等待其他线程到达</span><br><span class="line">pool-1-thread-4 到达珊阑处，等待其他线程到达</span><br><span class="line">pool-1-thread-5 到达珊阑处，等待其他线程到达</span><br><span class="line">pool-1-thread-6 到达珊阑处，等待其他线程到达</span><br><span class="line">pool-1-thread-7 到达珊阑处，等待其他线程到达</span><br><span class="line">pool-1-thread-8 到达珊阑处，等待其他线程到达</span><br><span class="line">pool-1-thread-9 到达珊阑处，等待其他线程到达</span><br><span class="line">pool-1-thread-10 到达珊阑处，等待其他线程到达</span><br><span class="line">所有线程已经到达，可以在这里面做一些东西</span><br><span class="line">pool-1-thread-10 所有线程到达，开始向下执行</span><br><span class="line">pool-1-thread-1 所有线程到达，开始向下执行</span><br><span class="line">pool-1-thread-2 所有线程到达，开始向下执行</span><br><span class="line">pool-1-thread-3 所有线程到达，开始向下执行</span><br><span class="line">pool-1-thread-4 所有线程到达，开始向下执行</span><br><span class="line">pool-1-thread-5 所有线程到达，开始向下执行</span><br><span class="line">pool-1-thread-6 所有线程到达，开始向下执行</span><br><span class="line">pool-1-thread-7 所有线程到达，开始向下执行</span><br><span class="line">pool-1-thread-9 所有线程到达，开始向下执行</span><br><span class="line">pool-1-thread-8 所有线程到达，开始向下执行</span><br></pre></td></tr></table></figure><h3 id="Semaphore"><a href="#Semaphore" class="headerlink" title="Semaphore"></a>Semaphore</h3><p>通过构造方法可以指定可用资源数量，线程在执行的时候，通过<code>semaphore.acquire()</code>获取资源，如果获取到，继续执行，如果获取不到，就会阻塞当前线程，直到其他线程通过<code>semaphore.release()</code>释放资源。</p><h4 id="使用场景-2"><a href="#使用场景-2" class="headerlink" title="使用场景"></a>使用场景</h4><p>池化技术 比如对象池 连接池 缓存池等。多个线程获取有限资源的情况</p><p>下面的代码，十个线程抢占三个可用资源，剩下的7个线程会被阻塞住，直到资源使用完毕：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SemphoreTest</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> <span class="number">10</span>;</span><br><span class="line">        <span class="type">ExecutorService</span> <span class="variable">fixedThreadPool</span> <span class="operator">=</span> Executors.newFixedThreadPool(count);</span><br><span class="line">        <span class="type">Semaphore</span> <span class="variable">semaphore</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Semaphore</span>(<span class="number">3</span>);</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; count; i++) &#123;</span><br><span class="line">            <span class="type">SemphoreRunnable</span> <span class="variable">runnable</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SemphoreRunnable</span>((i + <span class="number">1</span>) * <span class="number">1000</span>,semaphore);</span><br><span class="line">            fixedThreadPool.execute(runnable);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">SemphoreRunnable</span> <span class="keyword">implements</span> <span class="title class_">Runnable</span>&#123;</span><br><span class="line">        <span class="keyword">private</span> Semaphore semaphore;</span><br><span class="line">        <span class="keyword">private</span> <span class="type">long</span> sleepTime;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">SemphoreRunnable</span><span class="params">(<span class="type">long</span> sleepTime, Semaphore semaphore)</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.semaphore = semaphore;</span><br><span class="line">            <span class="built_in">this</span>.sleepTime = sleepTime;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                semaphore.acquire();</span><br><span class="line">                System.out.println(Thread.currentThread().getName() + <span class="string">&quot; 占用一个资源&quot;</span>);</span><br><span class="line">                TimeUnit.MILLISECONDS.sleep(sleepTime);</span><br><span class="line">                System.out.println(Thread.currentThread().getName() + <span class="string">&quot;资源使用完毕，释放资源&quot;</span>);</span><br><span class="line">                semaphore.release();</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>执行结果：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">pool-1-thread-2 占用一个资源</span><br><span class="line">pool-1-thread-3 占用一个资源</span><br><span class="line">pool-1-thread-6 占用一个资源</span><br><span class="line">pool-1-thread-2资源使用完毕，释放资源</span><br><span class="line">pool-1-thread-4 占用一个资源</span><br><span class="line">pool-1-thread-3资源使用完毕，释放资源</span><br><span class="line">pool-1-thread-5 占用一个资源</span><br><span class="line">pool-1-thread-6资源使用完毕，释放资源</span><br><span class="line">pool-1-thread-4资源使用完毕，释放资源</span><br><span class="line">pool-1-thread-1 占用一个资源</span><br><span class="line">pool-1-thread-7 占用一个资源</span><br><span class="line">pool-1-thread-1资源使用完毕，释放资源</span><br><span class="line">pool-1-thread-8 占用一个资源</span><br></pre></td></tr></table></figure><h3 id="BlockingQueue"><a href="#BlockingQueue" class="headerlink" title="BlockingQueue"></a>BlockingQueue</h3><p>阻塞队列提供可阻塞的入队和出队操作，队列满了，入队操作就会阻塞直到有其他元素出队，队列空了，出队操作会阻塞直到有新的元素可用。</p><p><code>BlockingQueue</code>提供了一下几种操作：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">boolean</span> <span class="title function_">add</span><span class="params">(E e)</span>; <span class="comment">//增加一个元素 如果队列已满，则抛出一个IIIegaISlabEepeplian异常</span></span><br><span class="line"><span class="type">boolean</span> <span class="title function_">offer</span><span class="params">(E e)</span>; <span class="comment">//添加一个元素，成功返回true，失败返回false，不会阻塞</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">put</span><span class="params">(E e)</span>;  <span class="comment">//添加一个元素，如果队列已满，则阻塞</span></span><br><span class="line">E <span class="title function_">take</span><span class="params">()</span>;  <span class="comment">//移除并返回队列头部的元素，如果队列为空，则阻塞</span></span><br><span class="line">E <span class="title function_">poll</span><span class="params">()</span>;  <span class="comment">//移除并返回队列头部的元素，如果队列为空，则直接返回null，不会阻塞</span></span><br><span class="line"><span class="type">boolean</span> <span class="title function_">remove</span><span class="params">(Object o)</span>; <span class="comment">//移除并返回队列头部的元素，如果队列为空，则抛出一个NoSuchElementException异常</span></span><br></pre></td></tr></table></figure><p>其中，实现了<code>BlockingQueue</code>的接口有下面几个队列：</p><ul><li>ArrayBlockingQueue：数组实现的有界队列，初始化必须制定容量，并且可以选择是否需要公平性（通过设置ReentrantLock来实现公平锁）</li><li>LinkedBlockingQueue ：链表实现的有界队列，在不指定容量的时候，大小为Integer.MAX_VALUE。</li><li>PriorityBlockingQueue ：支持优先级的无界队列，元素按照优先级顺序移除，因为是无界的，所以put不会被阻塞</li><li>DelayQueue ：优先级支持的、基于时间调度的队列，元素只有在延迟期满了，才能从中获取到</li><li>SynchronousQueue：简单聚集队列</li></ul><p>下面使用<code>ArrayBlockingQueue</code>实现生产者-消费者模型，生产者每隔1秒生产一个产品，10个消费者消费产品，每隔1秒，只有一个消费者获取产品消费，其他消费者只能等待：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BlockQueueTest</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        BlockingQueue&lt;String&gt; blockingQueue = <span class="keyword">new</span> <span class="title class_">ArrayBlockingQueue</span>&lt;&gt;(<span class="number">10</span>);</span><br><span class="line">        Executors.newSingleThreadExecutor().submit(<span class="keyword">new</span> <span class="title class_">Producer</span>(blockingQueue));</span><br><span class="line">        <span class="type">ExecutorService</span> <span class="variable">executorService</span> <span class="operator">=</span> Executors.newFixedThreadPool(<span class="number">10</span>);</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;</span><br><span class="line">            executorService.submit(<span class="keyword">new</span> <span class="title class_">Consumer</span>(blockingQueue));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Consumer</span> <span class="keyword">implements</span> <span class="title class_">Runnable</span>&#123;</span><br><span class="line">        <span class="keyword">private</span> BlockingQueue&lt;String&gt; blockingQueue;</span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">Consumer</span><span class="params">(BlockingQueue&lt;String&gt; blockingQueue)</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.blockingQueue = blockingQueue;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">while</span> (<span class="literal">true</span>)&#123;</span><br><span class="line">                    TimeUnit.MILLISECONDS.sleep(<span class="number">1000</span>);</span><br><span class="line">                    System.out.println(<span class="string">&quot;准备消费产品&quot;</span>);</span><br><span class="line">                    System.out.println(Thread.currentThread().getName() + <span class="string">&quot; 开始消费产品 &quot;</span> + blockingQueue.take());</span><br><span class="line">                    System.out.println(Thread.currentThread().getName() + <span class="string">&quot;消费结束&quot;</span>);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Producer</span> <span class="keyword">implements</span> <span class="title class_">Runnable</span>&#123;</span><br><span class="line">        <span class="keyword">private</span> BlockingQueue&lt;String&gt; blockingQueue;</span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">Producer</span><span class="params">(BlockingQueue&lt;String&gt; blockingQueue)</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.blockingQueue = blockingQueue;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">while</span> (<span class="literal">true</span>)&#123;</span><br><span class="line">                    TimeUnit.MILLISECONDS.sleep(<span class="number">1000</span>);</span><br><span class="line">                    <span class="type">SimpleDateFormat</span> <span class="variable">dateFormat</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SimpleDateFormat</span>(<span class="string">&quot;HH:mm:ss&quot;</span>);</span><br><span class="line">                    <span class="type">String</span> <span class="variable">product</span> <span class="operator">=</span> dateFormat.format(<span class="keyword">new</span> <span class="title class_">Date</span>());</span><br><span class="line">                    System.out.println(Thread.currentThread().getName() + <span class="string">&quot; 生产产品&quot;</span>);</span><br><span class="line">                    blockingQueue.put(product);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">                Thread.currentThread().interrupt();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>执行结果：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">准备消费产品</span><br><span class="line">准备消费产品</span><br><span class="line">准备消费产品</span><br><span class="line">准备消费产品</span><br><span class="line">准备消费产品</span><br><span class="line">准备消费产品</span><br><span class="line">准备消费产品</span><br><span class="line">准备消费产品</span><br><span class="line">准备消费产品</span><br><span class="line">准备消费产品</span><br><span class="line">pool-1-thread-1 生产产品</span><br><span class="line">pool-2-thread-3 开始消费产品 00:10:40</span><br><span class="line">pool-2-thread-3消费结束</span><br><span class="line">准备消费产品</span><br><span class="line">pool-1-thread-1 生产产品</span><br><span class="line">pool-2-thread-6 开始消费产品 00:10:41</span><br><span class="line">pool-2-thread-6消费结束</span><br><span class="line">准备消费产品</span><br><span class="line">pool-1-thread-1 生产产品</span><br><span class="line">pool-2-thread-1 开始消费产品 00:10:42</span><br><span class="line">pool-2-thread-1消费结束</span><br></pre></td></tr></table></figure><h3 id="FutureTask"><a href="#FutureTask" class="headerlink" title="FutureTask"></a>FutureTask</h3><p>用于执行一个可返回结果的长任务，任务在单独的线程中执行，其他线程可以用 get 方法取任务结果，如果任务尚未完成，线程会阻塞。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">FutureTaskTest</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        FutureTask&lt;String&gt; futureTask = <span class="keyword">new</span> <span class="title class_">FutureTask</span>((Callable&lt;String&gt;) () -&gt; &#123;</span><br><span class="line">            TimeUnit.MILLISECONDS.sleep(<span class="number">1000</span>);</span><br><span class="line">            <span class="type">Random</span> <span class="variable">random</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Random</span>();</span><br><span class="line">            <span class="type">boolean</span> <span class="variable">b</span> <span class="operator">=</span> random.nextBoolean();</span><br><span class="line">            <span class="keyword">return</span> b?<span class="string">&quot;success&quot;</span>:<span class="string">&quot;fail&quot;</span>;</span><br><span class="line">        &#125;);</span><br><span class="line">        <span class="comment">//开一个子线程运行任务</span></span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(futureTask).start();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;主线程执行其他业务。。。&quot;</span>);</span><br><span class="line">            TimeUnit.MILLISECONDS.sleep(<span class="number">1000</span>);</span><br><span class="line">            System.out.println(<span class="string">&quot;开始获取子线程运行结果。。。&quot;</span>);</span><br><span class="line">            <span class="type">String</span> <span class="variable">result</span> <span class="operator">=</span> futureTask.get();</span><br><span class="line">            <span class="keyword">if</span> (<span class="string">&quot;success&quot;</span>.equals(result))&#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;任务执行成功&quot;</span>);</span><br><span class="line">            &#125;<span class="keyword">else</span> &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;任务执行失败&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (ExecutionException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>执行结果：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">主线程执行其他业务。。。</span><br><span class="line">开始获取子线程运行结果。。。</span><br><span class="line">任务执行失败</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;线程同步工具主要包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;闭锁(CountDownLatch)&lt;/li&gt;
&lt;li&gt;栅栏(CyclicBarrier)&lt;/li&gt;
&lt;li&gt;信号量(Semaphore)&lt;/li&gt;
&lt;li&gt;阻塞队列(BlockingQueue)&lt;/li&gt;
&lt;li&gt;Futu</summary>
      
    
    
    
    <category term="java" scheme="http://example.com/categories/java/"/>
    
    
  </entry>
  
  <entry>
    <title>开源组件OnlyOffice搭建文档服务</title>
    <link href="http://example.com/2021/07/30/%E5%BC%80%E6%BA%90%E7%BB%84%E4%BB%B6OnlyOffice%E6%90%AD%E5%BB%BA%E6%96%87%E6%A1%A3%E6%9C%8D%E5%8A%A1/"/>
    <id>http://example.com/2021/07/30/%E5%BC%80%E6%BA%90%E7%BB%84%E4%BB%B6OnlyOffice%E6%90%AD%E5%BB%BA%E6%96%87%E6%A1%A3%E6%9C%8D%E5%8A%A1/</id>
    <published>2021-07-30T07:25:14.000Z</published>
    <updated>2025-09-15T07:26:55.171Z</updated>
    
    <content type="html"><![CDATA[<p>我司目前使用的文档处理中心就是基于only office搭建的集群，个人觉得还是比较好用的。</p><h2 id="1-安装"><a href="#1-安装" class="headerlink" title="1. 安装"></a>1. 安装</h2><p>官网：<a href="https://www.onlyoffice.com/zh/download.aspx">https://www.onlyoffice.com/zh/download.aspx</a></p><p>提供了多种安装方式，为了方便直接使用docker安装。</p><p>拉取only office镜像：</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker pull onlyoffice/documentserver</span><br></pre></td></tr></table></figure><p>启动docker容器：</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">docker run -i -t -d -p 6831:80 --restart=always \</span><br><span class="line"> --name onlyoffice-document-server \</span><br><span class="line"> -v /app/onlyoffice/DocumentServer/data:/var/www/onlyoffice/Data \</span><br><span class="line"> -v /app/onlyoffice/DocumentServer/logs:/var/log/onlyoffice \</span><br><span class="line"> -v /app/onlyoffice/DocumentServer/lib:/var/lib/onlyoffice \</span><br><span class="line"> onlyoffice/documentserver</span><br></pre></td></tr></table></figure><p>安装ftp服务：</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker run -d -p 21:21 -p 20:20 -p 21100-21110:21100-21110 -v F:/OnlyOfficeFile:/home/vsftpd -e FTP_USER=lingxiao -e FTP_PASS=12345678 -e PASV_ADDRESS=192.168.26.123 -e PASV_MIN_PORT=21100 -e PASV_MAX_PORT=21110 --name vsftpd --restart=always fauria/vsftpd</span><br></pre></td></tr></table></figure><p>-e FTP_USER&#x3D;davion -e FTP_PASS&#x3D;davion 添加一个初始化用户davion</p><p>PASV_MIN_PORT和PASV_MAX_PORT映射的是被动模式下端口使用范围</p><h2 id="2-下载最佳示例"><a href="#2-下载最佳示例" class="headerlink" title="2. 下载最佳示例"></a>2. 下载<del>最佳</del>示例</h2><p>下载地址：<a href="https://api.onlyoffice.com/editors/example/java">https://api.onlyoffice.com/editors/example/java</a></p><p>这个示例的代码看起来还是比较繁琐的，还用的servlet那套东西，而且由于是本地使用，docker访问宿主机网络有问题，于是我自己写了一个SpringBoot版本的。</p><p>SpringBoot版本github地址：<a href="https://github.com/lingxiaoplus/offfice">https://github.com/lingxiaoplus/offfice</a></p><h3 id="修改配置"><a href="#修改配置" class="headerlink" title="修改配置"></a>修改配置</h3><p>源码下载下来，需要修改yml中的几个配置：</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">office:</span></span><br><span class="line">  <span class="attr">filesizeMax:</span> <span class="number">5242880</span></span><br><span class="line">  <span class="attr">saveRootPath:</span> <span class="string">D:/OnlyOfficeFile</span></span><br><span class="line">  <span class="attr">baseUrl:</span> <span class="string">http://localhost:6831</span></span><br><span class="line">  <span class="attr">docService:</span></span><br><span class="line">    <span class="attr">url:</span></span><br><span class="line">      <span class="attr">callbackServer:</span> <span class="string">http://192.168.170.113:8080/callback/office</span></span><br></pre></td></tr></table></figure><p>其中<code>saveRootPath</code>改为你本地存在的一个目录，这个目录没啥用，只是作为一个临时文件目录。</p><p><code>baseUrl</code>改为onlyoffice服务的ip+端口地址。</p><p><code>callbackServer</code>修改成你本机的ip，作为回调接口，保存文件内容的时候，only office会调用，一定要确保only office能访问到你本机。不然会给出弹框提示，无法保存文件：</p><p><img src="https://blog-1252348761.cos.ap-chengdu.myqcloud.com/office/%E6%97%A0%E6%B3%95%E4%BF%9D%E5%AD%98.png"></p><p>因为only office是装在docker中的，而docker和我本机用的不是同一个网络，only office没法下载本地的文件，所以我就直接把文件上传到七牛云oss，这里要配置一下七牛云：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">oss:</span></span><br><span class="line">  <span class="attr">type:</span> <span class="string">qiniu</span></span><br><span class="line">  <span class="attr">accessKey:</span> <span class="string">V_ZleIlYzHPjekmAuRvDkkM12f</span> <span class="comment"># 设置好账号的ACCESS_KEY和SECRET_KEY</span></span><br><span class="line">  <span class="attr">secretKey:</span> <span class="string">U8SndxcceqMZGIKvhfPL3grP75cJlOH</span></span><br><span class="line">  <span class="attr">bucketName:</span> <span class="string">office</span>  <span class="comment"># 要上传的UrlAccessDecisionManager空间</span></span><br><span class="line">  <span class="attr">prefixDomain:</span> <span class="string">http://oss.lingxiao.top</span></span><br><span class="line">  <span class="attr">rootPath:</span> <span class="string">/onlyOffice</span></span><br></pre></td></tr></table></figure><p>如果没有七牛云，也可以自己用minio本地搭一个对象存储服务，同样要保证文件链接是可访问的：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">oss:</span></span><br><span class="line">  <span class="attr">type:</span> <span class="string">minio</span></span><br><span class="line">  <span class="attr">accessKey:</span> <span class="string">admin</span> <span class="comment"># 设置好账号的ACCESS_KEY和SECRET_KEY</span></span><br><span class="line">  <span class="attr">secretKey:</span> <span class="string">admin123456</span></span><br><span class="line">  <span class="attr">bucketName:</span> <span class="string">office</span>  <span class="comment"># 要上传的UrlAccessDecisionManager空间</span></span><br><span class="line">  <span class="attr">prefixDomain:</span> <span class="string">http://oss.lingxiao.top</span></span><br><span class="line">  <span class="attr">rootPath:</span> <span class="string">/onlyOffice</span></span><br></pre></td></tr></table></figure><p>docker搭建minio对象存储服务：</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">docker run -p 9000:9000 --name minio_office \</span><br><span class="line">-d --restart=always \</span><br><span class="line">-e <span class="string">&quot;MINIO_ACCESS_KEY=admin&quot;</span> \</span><br><span class="line">-e <span class="string">&quot;MINIO_SECRET_KEY=admin123456&quot;</span> \</span><br><span class="line">-v D:\minio:/data \</span><br><span class="line">-v D:\minio\config:/root/.minio \</span><br><span class="line">minio/minio server /data</span><br></pre></td></tr></table></figure><p>保证文件的链接是office可访问的，不然会出现下面的提示无法下载文件：</p><p><img src="https://blog-1252348761.cos.ap-chengdu.myqcloud.com/office/%E6%97%A0%E6%B3%95%E4%B8%8B%E8%BD%BD.png"></p><h3 id="office的相关配置"><a href="#office的相关配置" class="headerlink" title="office的相关配置"></a>office的相关配置</h3><p>office相关配置都是在前端中控制的，首先要引入only office的js：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">type</span>=<span class="string">&quot;text/javascript&quot;</span> <span class="attr">th:src</span>=<span class="string">&quot;$&#123;docserviceApiUrl&#125;&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure><p>配置相关的都是用的他默认的：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">type</span>=<span class="string">&quot;text/javascript&quot;</span> <span class="attr">th:inline</span>=<span class="string">&quot;javascript&quot;</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">var</span> doca;</span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">var</span> innerAlert = <span class="keyword">function</span> (<span class="params">message</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">            <span class="keyword">if</span> (<span class="variable language_">console</span> &amp;&amp; <span class="variable language_">console</span>.<span class="property">log</span>)</span></span><br><span class="line"><span class="language-javascript">                <span class="variable language_">console</span>.<span class="title function_">log</span>(message);</span></span><br><span class="line"><span class="language-javascript">        &#125;;</span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">var</span> onReady = <span class="keyword">function</span> (<span class="params"></span>) &#123;</span></span><br><span class="line"><span class="language-javascript">            <span class="title function_">innerAlert</span>(<span class="string">&quot;Document editor ready&quot;</span>);</span></span><br><span class="line"><span class="language-javascript">        &#125;;</span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">var</span> onDocumentStateChange = <span class="keyword">function</span> (<span class="params">event</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">            <span class="keyword">var</span> title = <span class="variable language_">document</span>.<span class="property">title</span>.<span class="title function_">replace</span>(<span class="regexp">/\*$/g</span>, <span class="string">&quot;&quot;</span>);</span></span><br><span class="line"><span class="language-javascript">            <span class="variable language_">document</span>.<span class="property">title</span> = title + (event.<span class="property">data</span> ? <span class="string">&quot;*&quot;</span> : <span class="string">&quot;&quot;</span>);</span></span><br><span class="line"><span class="language-javascript">        &#125;;</span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">var</span> onRequestEditRights = <span class="keyword">function</span> (<span class="params"></span>) &#123;</span></span><br><span class="line"><span class="language-javascript">            location.<span class="property">href</span> = location.<span class="property">href</span>.<span class="title function_">replace</span>(<span class="title class_">RegExp</span>(<span class="string">&quot;mode=view\&amp;?&quot;</span>, <span class="string">&quot;i&quot;</span>), <span class="string">&quot;&quot;</span>);</span></span><br><span class="line"><span class="language-javascript">        &#125;;</span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">var</span> onError = <span class="keyword">function</span> (<span class="params">event</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">            <span class="keyword">if</span> (event)</span></span><br><span class="line"><span class="language-javascript">                <span class="title function_">innerAlert</span>(event.<span class="property">data</span>);</span></span><br><span class="line"><span class="language-javascript">        &#125;;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">var</span> onOutdatedVersion = <span class="keyword">function</span> (<span class="params">event</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">            location.<span class="title function_">reload</span>(<span class="literal">true</span>);</span></span><br><span class="line"><span class="language-javascript">        &#125;;</span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">const</span> file = <span class="comment">/*[[$&#123;file&#125;]]*/</span> &#123;&#125;;</span></span><br><span class="line"><span class="language-javascript">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;文件信息&quot;</span>,file)</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">var</span> сonnectEditor = <span class="keyword">function</span> (<span class="params"></span>) &#123;</span></span><br><span class="line"><span class="language-javascript">            <span class="keyword">var</span> config=&#123;</span></span><br><span class="line"><span class="language-javascript">                <span class="string">&quot;document&quot;</span>:&#123;</span></span><br><span class="line"><span class="language-javascript">                    <span class="string">&quot;fileType&quot;</span>: file.<span class="property">document</span>.<span class="property">fileType</span>,</span></span><br><span class="line"><span class="language-javascript">                    <span class="string">&quot;key&quot;</span>: file.<span class="property">document</span>.<span class="property">key</span>,</span></span><br><span class="line"><span class="language-javascript">                    <span class="string">&quot;title&quot;</span>: file.<span class="property">document</span>.<span class="property">title</span>,</span></span><br><span class="line"><span class="language-javascript">                    <span class="string">&quot;url&quot;</span>: file.<span class="property">document</span>.<span class="property">url</span>,</span></span><br><span class="line"><span class="language-javascript">                    <span class="string">&quot;permissions&quot;</span>: &#123;</span></span><br><span class="line"><span class="language-javascript">                        <span class="string">&quot;changeHistory&quot;</span>: <span class="literal">true</span>,</span></span><br><span class="line"><span class="language-javascript">                        <span class="string">&quot;comment&quot;</span>: <span class="literal">true</span>,</span></span><br><span class="line"><span class="language-javascript">                        <span class="string">&quot;copy&quot;</span>: <span class="literal">false</span>,</span></span><br><span class="line"><span class="language-javascript">                        <span class="string">&quot;download&quot;</span>: <span class="literal">true</span>,</span></span><br><span class="line"><span class="language-javascript">                        <span class="string">&quot;edit&quot;</span>: file.<span class="property">editorConfig</span>.<span class="property">editable</span>,</span></span><br><span class="line"><span class="language-javascript">                        <span class="string">&quot;fillForms&quot;</span>: <span class="literal">true</span>,</span></span><br><span class="line"><span class="language-javascript">                        <span class="string">&quot;modifyContentControl&quot;</span>: <span class="literal">true</span>,</span></span><br><span class="line"><span class="language-javascript">                        <span class="string">&quot;modifyFilter&quot;</span>: <span class="literal">true</span>,</span></span><br><span class="line"><span class="language-javascript">                        <span class="string">&quot;print&quot;</span>: <span class="literal">true</span>,</span></span><br><span class="line"><span class="language-javascript">                        <span class="string">&quot;rename&quot;</span>: <span class="literal">true</span>,</span></span><br><span class="line"><span class="language-javascript">                        <span class="string">&quot;review&quot;</span>: <span class="literal">true</span></span></span><br><span class="line"><span class="language-javascript">                    &#125;,</span></span><br><span class="line"><span class="language-javascript">                &#125;,</span></span><br><span class="line"><span class="language-javascript">                <span class="string">&quot;documentType&quot;</span>: file.<span class="property">documentType</span>,</span></span><br><span class="line"><span class="language-javascript">                <span class="string">&quot;editorConfig&quot;</span>: &#123;</span></span><br><span class="line"><span class="language-javascript">                    <span class="string">&quot;callbackUrl&quot;</span>: file.<span class="property">editorConfig</span>.<span class="property">callbackUrl</span>,</span></span><br><span class="line"><span class="language-javascript">                    <span class="string">&quot;customization&quot;</span>:&#123;</span></span><br><span class="line"><span class="language-javascript">                        <span class="string">&quot;forcesave&quot;</span>:<span class="string">&quot;true&quot;</span></span></span><br><span class="line"><span class="language-javascript">                    &#125;,</span></span><br><span class="line"><span class="language-javascript">                    <span class="string">&quot;user&quot;</span>: &#123;</span></span><br><span class="line"><span class="language-javascript">                        <span class="string">&quot;id&quot;</span>: <span class="string">&quot;78ele841&quot;</span>,</span></span><br><span class="line"><span class="language-javascript">                        <span class="string">&quot;name&quot;</span>:<span class="string">&quot;John Smith&quot;</span></span></span><br><span class="line"><span class="language-javascript">                    &#125;,</span></span><br><span class="line"><span class="language-javascript">                    <span class="string">&quot;lang&quot;</span>:<span class="string">&quot;zh-CN&quot;</span>,</span></span><br><span class="line"><span class="language-javascript">                &#125;,</span></span><br><span class="line"><span class="language-javascript">                <span class="string">&quot;height&quot;</span>: <span class="string">&quot;100%&quot;</span>,</span></span><br><span class="line"><span class="language-javascript">                <span class="string">&quot;width&quot;</span>: <span class="string">&quot;100%&quot;</span></span></span><br><span class="line"><span class="language-javascript">            &#125;;</span></span><br><span class="line"><span class="language-javascript">            docEditor = <span class="keyword">new</span> <span class="title class_">DocsAPI</span>.<span class="title class_">DocEditor</span>(<span class="string">&quot;iframeEditor&quot;</span>, config);</span></span><br><span class="line"><span class="language-javascript">        &#125;;</span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">if</span> (<span class="variable language_">window</span>.<span class="property">addEventListener</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">            <span class="variable language_">window</span>.<span class="title function_">addEventListener</span>(<span class="string">&quot;load&quot;</span>, сonnectEditor);</span></span><br><span class="line"><span class="language-javascript">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (<span class="variable language_">window</span>.<span class="property">attachEvent</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">            <span class="variable language_">window</span>.<span class="title function_">attachEvent</span>(<span class="string">&quot;load&quot;</span>, сonnectEditor);</span></span><br><span class="line"><span class="language-javascript">        &#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    </span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="文件转换"><a href="#文件转换" class="headerlink" title="文件转换"></a>文件转换</h3><p>有些文件不需要经过转换，就能直接预览的，比如pdf文件。但是如果是doc、xls、pptx这种类型的文件，就需要经过only office先转换一次，然后才能编辑。</p><p>所以文件上传的时候做一下判断，然后再看是否要传给only office做一次转换，转换完成之后将转换的文件从only office下载回来保存，下次预览就用这个转换后的文件做预览。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><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="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> ResponseResult&lt;ConvertResult&gt; <span class="title function_">convert</span><span class="params">(String fileName,String fileUrl)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">fileExt</span> <span class="operator">=</span> fileUtil.getFileExtension(fileName);</span><br><span class="line">        <span class="type">FileType</span> <span class="variable">fileType</span> <span class="operator">=</span> fileUtil.getFileType(fileName);</span><br><span class="line">        <span class="type">String</span> <span class="variable">internalFileExt</span> <span class="operator">=</span> DocumentManager.getInternalExtension(fileType);</span><br><span class="line">        <span class="keyword">if</span> (documentManager.getViewedSuffixes().contains(fileExt))&#123;</span><br><span class="line">            <span class="type">ConvertResult</span> <span class="variable">convertResult</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ConvertResult</span>();</span><br><span class="line">            convertResult.setFileUrl(fileUrl);</span><br><span class="line">            convertResult.setFileName(fileName);</span><br><span class="line">            convertResult.setProgress(<span class="number">100</span>);</span><br><span class="line">            convertResult.setEndConvert(<span class="literal">true</span>);</span><br><span class="line">            <span class="keyword">return</span> ResponseResult.ok(convertResult);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (!documentManager.getConvertSuffixes().contains(fileExt)) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">OfficeException</span>(<span class="string">&quot;不支持的文件格式: &quot;</span>+ fileExt);</span><br><span class="line">        &#125;</span><br><span class="line">        log.debug(<span class="string">&quot;开始文件转换: &#123;&#125;&quot;</span>,fileUrl);</span><br><span class="line">        <span class="type">String</span> <span class="variable">key</span> <span class="operator">=</span> serviceConverter.generateRevisionId(fileUrl);</span><br><span class="line">        <span class="type">ConvertResult</span> <span class="variable">convertResult</span> <span class="operator">=</span> serviceConverter.getConvertedUri(fileUrl, fileExt, internalFileExt, key, <span class="literal">true</span>);</span><br><span class="line">        <span class="keyword">if</span> (!convertResult.getEndConvert()) &#123;</span><br><span class="line">            <span class="comment">//没有转换完成</span></span><br><span class="line">            <span class="keyword">return</span> ResponseResult.ok(convertResult);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//转换完成，可以下载转换后的文件了</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">correctName</span> <span class="operator">=</span> documentManager.getCorrectName(fileUtil.getFileNameWithoutExtension(fileName) + internalFileExt);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">File</span> <span class="variable">convertedFile</span> <span class="operator">=</span> httpUtil.downloadFile(convertResult.getFileUrl(), documentManager.storagePath(correctName));</span><br><span class="line">            log.debug(<span class="string">&quot;文件转换成功，上传转换文件...&quot;</span>);</span><br><span class="line">            <span class="type">OssFileInfo</span> <span class="variable">ossFileInfo</span> <span class="operator">=</span> ossFileService.uploadFile(convertedFile, CONVERT_PATH,<span class="literal">false</span>);</span><br><span class="line">            convertedFile.deleteOnExit();</span><br><span class="line">            log.debug(<span class="string">&quot;文件转换成功: &#123;&#125;&quot;</span>,ossFileInfo);</span><br><span class="line">            convertResult.setFileUrl(ossFileInfo.getPath());</span><br><span class="line">            <span class="keyword">return</span> ResponseResult.ok(convertResult);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IOException ex) &#123;</span><br><span class="line">            ex.printStackTrace();</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">OfficeException</span>(ex.getMessage());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><h2 id="3-总结"><a href="#3-总结" class="headerlink" title="3. 总结"></a>3. 总结</h2><p>上面也只是涉及了文件编辑相关的，多人协作编辑还没涉及，所以博主也只了解文件编辑相关的原理。各位想深入理解的可以去官网看原文。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;我司目前使用的文档处理中心就是基于only office搭建的集群，个人觉得还是比较好用的。&lt;/p&gt;
&lt;h2 id=&quot;1-安装&quot;&gt;&lt;a href=&quot;#1-安装&quot; class=&quot;headerlink&quot; title=&quot;1. 安装&quot;&gt;&lt;/a&gt;1. 安装&lt;/h2&gt;&lt;p&gt;官网：&lt;a </summary>
      
    
    
    
    <category term="开源组件" scheme="http://example.com/categories/%E5%BC%80%E6%BA%90%E7%BB%84%E4%BB%B6/"/>
    
    
  </entry>
  
  <entry>
    <title>MySQL是怎样运行的</title>
    <link href="http://example.com/2021/07/15/MySQL%E6%98%AF%E6%80%8E%E6%A0%B7%E8%BF%90%E8%A1%8C%E7%9A%84/"/>
    <id>http://example.com/2021/07/15/MySQL%E6%98%AF%E6%80%8E%E6%A0%B7%E8%BF%90%E8%A1%8C%E7%9A%84/</id>
    <published>2021-07-15T07:23:31.000Z</published>
    <updated>2025-09-15T07:26:55.166Z</updated>
    
    <content type="html"><![CDATA[<h2 id="字符集和比较规则"><a href="#字符集和比较规则" class="headerlink" title="字符集和比较规则"></a>字符集和比较规则</h2><h3 id="字符集"><a href="#字符集" class="headerlink" title="字符集"></a>字符集</h3><p>计算机中只能存储二进制数据，那怎么存储字符呢？建立字符与二进制数据之间的映射关系就行了。</p><p>一个字符映射成一个二进制数据的过程叫做编码，反之叫做解码。</p><p>比如’a’ -&gt; 00000001 (十六进制：0x01)这样的映射关系。人们将这样的映射关系抽象出一个字符集的概念来描述某个字符范围的编码规则。</p><h4 id="比较规则"><a href="#比较规则" class="headerlink" title="比较规则"></a>比较规则</h4><p>比较两个字符的大小，容易想到的方法是比较两个字符对应的二进制数字大小，这个规则比较简单，但是不符合现实需求，比如’a’和’A’我们认为是相等的，这个时候就不适合使用这种方法了，可以先将字符全部转为大写或者小写，然后再比较。但是如果是中文字符呢？所以说，<strong>同一种字符集可以有多种不同的比较规则</strong>。</p><h4 id="MySQL中的字符集"><a href="#MySQL中的字符集" class="headerlink" title="MySQL中的字符集"></a>MySQL中的字符集</h4><p>utf8字符集表示一个字符需要使用1-4个字节，但是我们常用的一些字符使用1-3个字节就可以表示了。MySQL中utf8实际上是被阉割过的字符集utf8mb3。utf8mb4才是正宗的utf8字符集。存储一些emoji表情之类的需要4个字节的，这个时候就只能使用utf8mb4字符集。</p><p>常用字符集（MaxLen表示该字符集一个字符最多需要几个字节）</p><table><thead><tr><th>字符集名称</th><th>MaxLen</th></tr></thead><tbody><tr><td>ascii</td><td>1</td></tr><tr><td>latin1</td><td>1</td></tr><tr><td>gb2312</td><td>2</td></tr><tr><td>gkb</td><td>2</td></tr><tr><td>utf8</td><td>3</td></tr><tr><td>utf8mb4</td><td>4</td></tr></tbody></table><h4 id="MySQL中的比较规则"><a href="#MySQL中的比较规则" class="headerlink" title="MySQL中的比较规则"></a>MySQL中的比较规则</h4><p>比较规则名称以与其关联的字符集的名称开头。比如utf8_general_ci、utf8_polish_ci。后边紧跟着该比较规则主要作用于哪种语言，utf8_polish_ci表示以波兰语的规则比较，utf8_general_ci表示通用的比较规则。</p><p>后缀意味着该比较规则是否区分语言中的重音、大小写啥的，具体可以用的值如下：</p><p>| 后缀  | 英文释义  | 描述  |<br>| ———- | —— |<br>| <code>_ai</code> |  <code>accent insensitive</code>|  不区分重音|<br>| <code>_as</code> |  <code>accent sensitive</code>|  区分重音|<br>| <code>_ci</code> | <code>case insensitive</code> | 不区分大小写 |<br>| <code>_cs</code>  |  <code>case sensitive</code>  | 区分大小写|<br>| <code>_bin</code> |  <code>binary</code> | 以二进制方式比较 |</p><h4 id="MySQL中的字符集和比较规则级别"><a href="#MySQL中的字符集和比较规则级别" class="headerlink" title="MySQL中的字符集和比较规则级别"></a>MySQL中的字符集和比较规则级别</h4><p>服务器级别</p><p>数据库级别</p><p>表级别，没有指定默认为数据库级别的字符集和比较规则</p><p>列级别，没有指定默认为表级别的字符集和比较规则</p><h3 id="字符的编解码"><a href="#字符的编解码" class="headerlink" title="字符的编解码"></a>字符的编解码</h3><p>从客户端发往服务器的请求本质上就是一个字符串，服务器向客户端返回的结果本质上也是一个字符串，而字符串其实是使用某种字符集编码的二进制数据。从发送请求到返回结果这个过程中伴随着多次字符集的转换，在这个过程中会用到3个系统变量。</p><table><thead><tr><th>系统变量</th><th>描述</th></tr></thead><tbody><tr><td>character_set_client</td><td>服务器解码请求时使用的字符集</td></tr><tr><td>character_set_connection</td><td>服务器处理请求时会把请求字符串从<code>character_set_client</code>转为<code>character_set_connection</code></td></tr><tr><td>character_set_results</td><td>服务器向客户端返回数据时使用的字符集</td></tr></tbody></table><p><img src="/charset_mysql.jpg"></p><p>服务器认为客户端发送过来的请求是用<code>character_set_client</code>编码的，假设你的客户端采用的字符集和 character_set_client 不一样的话，就会出现意想不到的结果。</p><p>服务器将把得到的结果集使用character_set_results编码后发送给客户端。假设你的客户端采用的字符集和 character_set_results 不一样的话，这就可能会出现客户端无法解码结果集的情况，结果就是在你的屏幕上出现乱码。</p><p>character_set_connection只是服务器在将请求的字节串从character_set_client转换为character_set_connection时使用，它是什么其实没多重要。</p><h2 id="InnoDB存储结构"><a href="#InnoDB存储结构" class="headerlink" title="InnoDB存储结构"></a>InnoDB存储结构</h2><p><code>InnoDB</code>是一个将表中的数据存储到磁盘上的存储引擎，所以即使关机后重启我们的数据还是存在的。</p><p>真正处理数据的过程是发生在内存中的，所以需要把磁盘中的数据加载到内存中，或者从内存中同步数据回磁盘中。这样如果数据是一条一条的读，效率是非常慢的，所以InnoDB采取的方式是将数据划分为若干页，以页作为磁盘和内存之间的基本单位，InnoDB中页的大小一般为16kb。</p><h3 id="行格式"><a href="#行格式" class="headerlink" title="行格式"></a>行格式</h3><p>我们平时是以记录为单位来向表中插入数据的，这些记录在磁盘上的存放方式也被称为<code>行格式</code>或者<code>记录格式</code>。目前为止行格式共有四种，分别是：Compact、Redundant、Dynamic和Compressed。</p><p>Compact行格式结构：</p><p><img src="https://blog-1252348761.cos.ap-chengdu.myqcloud.com/diagram/COMPACT%E8%A1%8C%E6%A0%BC%E5%BC%8F.png"></p><p>变长字段长度列表：</p><p>MySQL支持一些变长的数据类型，比如VARCHAR(M)，TEXT类型等，使用这些字段存储多少字节的数据是不固定的，所以我们在存储真实数据的时候需要顺便把这些数据占用的字节数也存起来。</p><p>NULL值列表：</p><p>某些列可能存在NULL值，如果把NULL值存在记录的真实地址，那么就比较浪费存储空间。所以直接把列值为NULL的记录统一存在NULL值列表。</p><p>NULL值列表必须用整数个字节的位表示，如果使用的二进制位个数不是整数个字节，则在字节的高位补0。一个字节8bit，如果列c1,c2,c3为NULL，那么应该是这样的：</p><table><thead><tr><th></th><th></th><th></th><th></th><th></th><th>c3</th><th>c2</th><th>c1</th></tr></thead><tbody><tr><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>1</td><td>1</td><td>1</td></tr></tbody></table><p>记录头信息</p><p>记录当前的位置信息，还有一些预留位</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;字符集和比较规则&quot;&gt;&lt;a href=&quot;#字符集和比较规则&quot; class=&quot;headerlink&quot; title=&quot;字符集和比较规则&quot;&gt;&lt;/a&gt;字符集和比较规则&lt;/h2&gt;&lt;h3 id=&quot;字符集&quot;&gt;&lt;a href=&quot;#字符集&quot; class=&quot;headerlink&quot; ti</summary>
      
    
    
    
    <category term="java" scheme="http://example.com/categories/java/"/>
    
    
  </entry>
  
  <entry>
    <title>两年后端的面试分享</title>
    <link href="http://example.com/2021/06/15/%E4%B8%A4%E5%B9%B4%E5%90%8E%E7%AB%AF%E7%9A%84%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/"/>
    <id>http://example.com/2021/06/15/%E4%B8%A4%E5%B9%B4%E5%90%8E%E7%AB%AF%E7%9A%84%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/</id>
    <published>2021-06-15T07:22:21.000Z</published>
    <updated>2025-09-15T07:26:55.173Z</updated>
    
    <content type="html"><![CDATA[<p>从今年5月份就开始面试了，前前后后也有4个多月了吧，面试了几十家公司，其中有几十个人的小型创业公司，也有几百人的上市公司，踩了不少的坑，最后收到了宝能集团的offer，我也决定就去这家了。</p><p>看过很多别人的面试分享经验，这里我也做一些面试的分享吧。</p><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>首先，提前要求自带简历的一般都不太靠谱，特别是小公司要求自带简历，一般都是hr不专业，不想提前把你的简历打印出来，或者说不想为了你浪费几张A4纸，所以这种公司可以直接pass了，除非你特别缺offer，生存都成困难了。</p><p>其次，可以提前问一下hr面试流程是什么样的，现在很多公司都取消笔试了。众所周知笔试没有人监督你，所以可以掏出手机(虽然我没做过，但是面试看见其他人这样做过)，所以我觉得笔试作用其实没多少。如果有公司是明确要先笔试，笔试通过再进行面试，而你面试的不是应届生岗位，就要小心了，先考虑一下那家公司过去面试距离远不远，提前去看准网看一下相关的评价，再考虑要不要去面试的事。我之前面过两家都是需要笔试的，笔试没通过是直接没有后面的面试的（有的面试题非常脑残，下面上图），来回也比较远，浪费了我一个上午的时间。</p><img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ded19ef5ab1b43bc81942a581f6bfe51~tplv-k3u1fbpfcp-zoom-1.image" style="zoom: 33%;" /><h2 id="面试准备"><a href="#面试准备" class="headerlink" title="面试准备"></a>面试准备</h2><p>面试的时候99.99%的公司都会让你做一下自我介绍和最近做的一个项目，所以自我介绍和项目要提前打好草稿，特别是不太会说话的。</p><h3 id="自我介绍"><a href="#自我介绍" class="headerlink" title="自我介绍"></a>自我介绍</h3><p>我觉得简洁一点就行了，一般面试官都会趁着你自我介绍的时候看简历，也没认真听你做自我介绍。</p><p>下面是我自己打的草稿：</p><blockquote><p>面试官你好，非常荣幸参加贵公司的本次面试。下面我简单的介绍一下我的个人情况，我叫凌霄，xx年毕业于xxx学院xxx专业。目前在xxx公司工作，主要是从事xxx开发。<br>通过xxx招聘平台关注到贵公司岗位的招聘信息，这个职位比较符合我未来的职业发展，而且贵公司的业务对我都非常有吸引力，非常希望能够进入贵公司发展。</p><p>我对技术有浓厚的兴趣，专业知识面宽，技术也相对全面，责任心强，思路清晰，沟通能力好，熟悉JAVA体系，<br>对各种开发流程和主流架构模式，设计模式和UML，有较强的需求分析、架构设计、项目管理能力，并有丰富的实战经验；<br>出于对工程师文化的向往，我在研发团队管理期间，尝试了多种方法去建立良性的团队氛围，并有效的改善了研发效率；<br>技术是为产品支撑的，所以我现在也在熟悉产品设计过程，希望可以结合公司发展战略，营销策略，跟着团队打造出一个好的产品。</p></blockquote><p>上面的可以适当简化一下，能说出来多少取决于你的记忆力。</p><h3 id="项目介绍"><a href="#项目介绍" class="headerlink" title="项目介绍"></a>项目介绍</h3><p>这个是真的要提前准备好说辞，不然你面试的时候边想边说，说起来磕磕巴巴的，你给面试官的第一印象就不好了，至少说明你的表述能力不强。</p><p>介绍项目的话可以不用太紧张，面试官才拿到你的简历看多久，所以肯定是对你自己的项目是最熟悉的。需要注意的地方就是不要只讲一些业务相关的，面试官不需要知道这些，他只关心你的技术能力是否足够，通过你的描述知道你使用了什么技术，再根据相关的技术进行提问。</p><p>贴一下我的草稿：</p><blockquote><p>我在xx公司做的xx系统，客户主要是xx，项目的功能主要是提供代理雨伞，代理快递，代理订餐，图书借阅等服务，后台用的是主体架构是使用Spring Cloud，微服务模块划分为xxx，我主要负责其中xxx几个模块的开发， orm用的是mybatis，数据库是用的mysql，缓存是用的redis，前台用到了vue技术。共有xx个同事参与开发，整个项目持续了xx时间。</p></blockquote><p>面试官会对他感兴趣的点进行提问。</p><h3 id="项目中遇到的难题"><a href="#项目中遇到的难题" class="headerlink" title="项目中遇到的难题"></a>项目中遇到的难题</h3><p>让我想起小学时经常写的作文：最难忘的一件事。这可真是太艹了，没想到小学学的知识现在派上了用场。</p><p>项目中遇到的难题，解决了之后其实都不是什么难题了。我列几个点可以参考一下：</p><ol><li>跨域</li><li>任务调度幂等性</li><li>使用mq遇到的问题</li><li>分布式系统中的事务问题</li><li>内存溢出问题</li></ol><p>当然最后还是要自己的专业技能扎实，才能获取源源不断的offer。</p><p><img src="//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3806dcc8ad064b9086d7bb49cb593cd4~tplv-k3u1fbpfcp-zoom-1.image"></p><h2 id="面试经验"><a href="#面试经验" class="headerlink" title="面试经验"></a>面试经验</h2><p>接下来分享一些这段时间的面试公司</p><h3 id="腾云亿想"><a href="#腾云亿想" class="headerlink" title="腾云亿想"></a>腾云亿想</h3><p>腾讯云全资子公司，其实就是腾讯云的外包公司</p><ol><li>数据库有哪几种索引</li><li>springboot原理，如何实现的自动装配</li><li>springboot打包成jar之后，怎么做到不重新打包修改他的properties配置文件</li><li>jvm调优做过吗，有哪些参数可以调，具体怎么调</li></ol><p>第一次面试，尴尬的是我基本上没打上来，只回答上了springboot原理，居然还叫我去hr面，要把这个岗位确定下来，我拒绝了。</p><h3 id="垒知科技研究院"><a href="#垒知科技研究院" class="headerlink" title="垒知科技研究院"></a>垒知科技研究院</h3><p>上市公司，原来叫做建研集团，做建筑相关的工程。</p><ol><li><p>你用过SpringBoot的哪些注解</p><ol><li>@component,@service,@controller,@controlleradvice，@Repository</li><li>@Autowired，@Inject，@Resource,@Qualifier，一般@Resource单独使用，@Qualifier会和@Autowired一起用</li><li>@dependsOn</li><li>@Configuration，@bean, @ComponentScan，</li><li>@Aspect，@PointCut，</li><li>@Scope，@PostConstruct，@PreDestory</li></ol></li><li><p>Spring的Bean默认是单例还是多例，我想改成多例的怎么办</p></li><li><p>项目中用到了rabbitmq，使用中遇到什么问题</p></li><li><p>rabbitmq重复消费怎么解决，除了数据库里查记录还有其他什么方法</p></li><li><p>分布式锁怎么实现，redis实现方式死锁怎么解决</p><p>我：数据库实现，redis实现，zookeeper实现。redis加上一个失效时间</p></li><li><p>mysql索引怎么实现的，btree索引有什么特点</p></li><li><p>现在有一个联合索引(A,B) 我有一个sql语句是</p><p>select xxx from xxx where A&#x3D;xxx 会用到索引吗</p><p>select xxx from xxx whereB&#x3D;xxx 会用到索引吗   为什么会是最左匹配原则</p></li><li><p>ArrayList实现了哪些接口</p><p>说了List、Cloneable、Serializable，我觉得他应该是想问RandomAccess接口，当时没回答上这个</p></li><li><p>代码优化怎么做的</p></li><li><p>nginx怎么配置负载均衡</p></li><li><p>闭锁怎么用，什么原理</p></li><li><p>二分查找法，时间复杂度</p></li></ol><p>自我感觉回答得不是很好，但是第二天收到了复试邀请，还挺惊讶的。</p><p>复试先是做一套性格测试题，然后就是项目主管来面试我，问了一下毕业学校，为啥学校挺好还是科班出身，为啥毕业会进小公司。然后问了下我现在的职位级别，接下来就巴拉巴拉开始夸他们公司。</p><p>然后就是cto面试，也没问技术，就问了问家里的情况，未来职业规划，然后说他是从华为出来的，公司实行狼性文化，加班是经常的。正题来了，他说待遇是不会高于我上家公司的，但是进他们公司未来的发展肯定会很好。我就有点不太舒服，就叹气嘛，然后他说我唉声叹息比较悲观，进公司会影响其他人，他需要的是一个时刻保持乐观的人，能给团队带来活力。</p><p><img src="//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9658c7471c21404096f0b63a104e3bb3~tplv-k3u1fbpfcp-zoom-1.image"></p><p>我整个人都傻了。</p><h3 id="西南凯亚"><a href="#西南凯亚" class="headerlink" title="西南凯亚"></a>西南凯亚</h3><p>中国民航的子公司，是一家国企。</p><p>先要提前一天做一套线上笔试题，不管有没有通过，第二天都是要去面试的。</p><p>主要是项目相关的，业务上的东西问得多</p><p>代码优化</p><p>mysql优化</p><p>jvm内存模型</p><p>spring cloud用过哪些组件 讲讲熔断机制</p><p>为什么离开上家公司</p><p>不是很难但是没有offer，心态崩了。</p><p><img src="//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4fde620be4374dbd8f463627774dd6e8~tplv-k3u1fbpfcp-zoom-1.image"></p><h3 id="睿沿科技"><a href="#睿沿科技" class="headerlink" title="睿沿科技"></a>睿沿科技</h3><p>做人脸识别相关的，和我上家公司做的产品差不多。</p><ol><li><p>Hashmap原理</p></li><li><p>ConcurrentHashMap原理，为什么多线程条件下性能这么好</p><p>我从sync在1.6做的优化谈的，他从锁的粒度方面谈的</p></li><li><p>项目相关</p></li><li><p>代码优化</p></li><li><p>mysql优化</p></li><li><p>spring的aop怎么实现的</p></li><li><p>每天抓拍的数据量比较大，有做过分库分表吗</p><p>用的shardingjdbc  还有mycat，中小公司一般用shardingjdbc，</p></li><li><p>Rabbitmq实现原理</p><p>不知道</p></li></ol><p>后面两个没有回答好吧，所以没有后续了。</p><h3 id="小海豚能源科技"><a href="#小海豚能源科技" class="headerlink" title="小海豚能源科技"></a>小海豚能源科技</h3><p>这家应该是要找做全栈的吧，开头就问我好几个前端相关的，我直接懵了，理所当然的没有后续了</p><ol><li><p>用过jquery哪些框架</p></li><li><p>es6语法熟悉吗</p></li><li><p>vue用过哪些组件</p></li><li><p>spring用过哪些注解</p></li><li><p>使用@autowired注入的时候，如果这个接口有多个实现，怎么指定注入哪一个的实现</p></li><li><p>eureka搭过集群吗</p></li><li><p>mysql优化，什么时候不会用到索引</p><ol><li><p>select *，可能会导致不走索引</p></li><li><p>where后面有函数运算，导致不走索引</p></li><li><p>索引不适用于隐式转换的情况，比如你的SELECT * FROM T WHERE Y &#x3D; 5 在Y上面有一个索引，但是Y列是VARCHAR2的，那么Oracle会将上面的5进行一个隐式的转换，SELECT * FROM T WHERE TO_NUMBER(Y) &#x3D; 5,这个时候也是有可能用不到索引的。</p></li><li><p>like查询使用通配符开头不会用到索引</p></li></ol><p>但是这些好像不是他想要的答案，我也猜不透。。</p></li><li><p>接受长期出差吗</p></li></ol><h3 id="开通金融"><a href="#开通金融" class="headerlink" title="开通金融"></a>开通金融</h3><p>做互联网金融的，公司总部在北京，我面试的时候是在成都的新网银行公司，看这个模式应该就是驻场开发了，但是公司本身不是外包。</p><ol><li>springboot和spring的不同</li><li>springboot特性，自动装配怎么实现的</li><li>spring用到哪些组件，具体场景</li><li>spring默认是单例还是多例，全局变量会有什么问题</li><li>spring事务传播行为、隔离级别</li><li>aop实现方式，jdk动态代理和cglib动态代理区别</li><li>jdk lambda表达式几种使用方式，取最大和最小值，list转map，list中有重复的怎么办</li><li>线程池 有哪些参数，各个参数的意义</li><li>ioc容器有什么优点</li><li>jvm 堆栈，区别，线程共享还是线程私有的</li><li>springcloud组件、用途，负载均衡的几种算法</li><li>分布式事务怎么做的</li><li>mybatis一对多，多对多怎么做</li><li>jpa和mybatis区别</li><li>vmstat命令干嘛的</li><li>redis查看内存占用命令</li><li>mysql导入大量数据怎么优化</li><li>mysql有一条数据死锁了，怎么排查问题，mysql表死锁，怎么排查</li><li>nginx反向代理是什么，正向代理呢</li></ol><p>这次面试回答得挺好的，hr也打了好几次电话说面试官对我挺有意向的，就是一顿操作猛如虎，一问工资0.9。而且我当时还是在职，要求我一周到岗，我就没去。</p><h3 id="兰亭集势"><a href="#兰亭集势" class="headerlink" title="兰亭集势"></a>兰亭集势</h3><p>做海外电商的公司，但是进去之后是做一些小系统的开发，问的问题有点像校招一样，我是应届毕业生说不定能答出来。</p><ol><li>看过开源框架源码吗，SpringBoot自动装配原理</li><li>写个冒泡，写个单例模式</li><li>mybatis和jdbc怎么防止sql注入</li><li>单一职责原则是什么，里式替换原则是什么</li><li>3NF是什么</li><li>接口定义一个变量，这个变量的默认作用域</li><li>有没有自己搭建过微服务</li><li>linux怎么看java进程，除了jps和top命令还有其他的吗</li><li>最近看什么书，我回答多线程并发相关的，他：没用，要在项目里用才有用</li><li>最近在学什么技术？docker，他：没用，很简单不需要学习</li><li>有个部门表，怎么设计他的字段，获取一个部门下的所有子部门，除了递归还有什么方法？加个code字段，通过部门前缀like查询</li></ol><h3 id="京东物流（自带简历）"><a href="#京东物流（自带简历）" class="headerlink" title="京东物流（自带简历）"></a>京东物流（自带简历）</h3><p>朋友内推的，听他说是稳了，去面就能过，结果还是翻车了。</p><ol><li><p>java特点描述一下，继承封装多态分别是什么意思</p></li><li><p>synchronized和lock区别</p></li><li><p>ConcurrentHashMap线程安全怎么实现的</p></li><li><p>创建线程的几种方式，创建线程池几个参数的含义，线程饱和策略</p></li><li><p>sleep和wait区别</p></li><li><p>spring是单例还是多例，多线程并发会出现什么问题</p></li><li><p>SpringBoot如何解决跨域，具体怎么配置</p></li><li><p>全局异常捕获怎么做的</p></li><li><p>SpringSecurity的前后端分离，登录怎么做的</p><p>我：拦截登录的url，然后获取到用户名和密码，把这个用户名和密码交给manager，manager会交给具体的provider处理，provider会调用UserDetailsService的方法完成验证。</p><p>这里是我失误了，他说用户名和密码时通过getattribute 获取到的吗，我说是的。</p></li><li><p>怎么设计一个安全的对外接口</p></li><li><p>单线程的redis为什么这么快</p></li><li><p>redis持久化方式</p></li><li><p>什么是聚簇索引，什么是非聚簇索引</p></li><li><p>mysql事务隔离级别</p></li><li><p>你觉得你能承担独立开发的任务吗</p></li></ol><p>其他问题回答得挺好的，自我感觉不错，本来以为能收到offer的，但是朋友说那个面试官觉得我不行，不能承担独立开发系统的任务，估计是第8个问题回答失误了吧，其他我觉得我回答没有问题。</p><h3 id="教育家网络科技"><a href="#教育家网络科技" class="headerlink" title="教育家网络科技"></a>教育家网络科技</h3><p>面试的人比较多，所以是群面，一个面试官对应多个面试者。虽然我回答得比另外一个面试者好很多，但是没有收到offer，很迷，真的很迷。</p><ol><li>介绍项目，然后基本上就围绕着项目的业务说</li><li>对金额的计算用什么类型，数据库中用什么类型存储</li><li>SpringBoot怎么自定义注解</li><li>mysql优化，like查询的时候，数据量很大，如果like查询以%开头，不会走索引，怎么优化。多表关联查询，怎么优化</li><li>代码优化怎么做的，平时重构从哪方面考虑</li></ol><h3 id="海康交通大数据"><a href="#海康交通大数据" class="headerlink" title="海康交通大数据"></a>海康交通大数据</h3><p>海康下面的一家分公司，主要做车辆交通相关大屏数据统计报表之类的，面试官和我一个学校的学长，开头就问我是哪个老师教的，吓我一跳哈哈哈哈，我以为给老师丢脸了。</p><ol><li>HashMap原理，底层结构，1.7和1.8区别，头插法还是尾插法、线程安全吗</li><li>ConcurentHashMap在什么地方加的锁，1.7和1.8区别，1.7用的segment，1.8用的synchronized</li><li>lock和synchronized这两个区别，1.6之后对synchronized做了什么优化</li><li>偏向锁、轻量级锁是怎么做到的，我回答的是每个对象都有对象头，对象头里做的标记，然后他问我对象头里有哪些信息</li><li>list和数组区别，哪个会内存泄漏，查询的话哪个快点。我觉得查询是一样快的，因为list本来底层就是数组</li><li>线程池各个参数介绍一下，原理是什么。你常用哪种线程池，为什么？</li><li>有哪些队列，你线程池一般用哪种队列</li><li>spring怎么解决循环依赖</li><li>jdk动态代理和cglib区别，哪个更快，有没有看过他们生成的字节码文件</li><li>cloud相关的组件用过哪些，hystrix熔断原理是什么，除了线程池还有什么。熔断有个半开状态，有什么用</li></ol><p>过程还是挺愉快的吧，就是薪资待遇没有谈到位，比较可惜。</p><h3 id="咻电科技（自带简历）"><a href="#咻电科技（自带简历）" class="headerlink" title="咻电科技（自带简历）"></a>咻电科技（自带简历）</h3><p>一家做共享充电宝的公司，面试官又是和我一个学校的学长，但是没有给我放水，很生气。</p><img src="//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6e0f8f79546c45048154a84d1408f683~tplv-k3u1fbpfcp-zoom-1.image"  /><p>问题：</p><ol><li>spring生命周期</li><li>线程池有几种创建方式</li><li>线程生命周期</li><li>eureka原理</li><li>lock和sync区别</li><li>有一个表有几个亿的数据，写sql分页查询怎么优化</li><li>redis内存淘汰策略有几种</li><li>dubbo熟悉吗</li><li>脏读幻读不可重复读，怎么解决</li><li>jquery冒泡了解吗</li></ol><p>最后问了一些学习相关的建议，收获挺多的。</p><h3 id="武汉佰钧成"><a href="#武汉佰钧成" class="headerlink" title="武汉佰钧成"></a>武汉佰钧成</h3><p>外包公司，给平安做的一个项目，电话面试，外包都喜欢这种方式。</p><ol><li>设计模式用过哪些</li><li>代码优化怎么做的</li><li>bio和nio区别</li><li>springcloud用过哪些组件</li><li>mysql优化，有做过sql从几秒优化到几十毫秒的吗</li><li>项目并发量多大</li><li>里式替换原则是什么</li><li>tpc&#x2F;ip协议</li><li>性能优化怎么做</li></ol><p>外包不是很好进吗，但是我没有收到offer。</p><h3 id="成都房联云码"><a href="#成都房联云码" class="headerlink" title="成都房联云码"></a>成都房联云码</h3><ol><li>你怎么看单例模式，写一个双重锁检测的单例模式</li><li>string、stringbuilder、stringbuffer区别</li><li>arraylist、linkedlist区别</li><li>hashmap1.7 、1.8底层结构区别</li><li>hashmap、hashtable区别</li><li>线程的生命周期</li><li>spring事务传播行为，底层实现原理</li><li>声明bean的几种方式</li><li>spring默认是单例还是多例，怎么实现多例</li><li>springmvc流程</li><li>jvm内存模型</li><li>lock和synchronized区别，最大的区别是</li><li>代码优化和sql优化</li><li>分布式，session怎么解决</li><li>redis优点</li><li>对外接口怎么保证安全，光是md5签名加密不行，还有其他方法吗</li><li>有一个功能模块，你会怎么安排时间，如果有一个紧急任务完成不了怎么办</li><li>平时怎么学习的</li></ol><p>面完问了一下期望薪资，然后约了二面，二面就简单问了一下项目相关的，平时怎么学习的。三面就是hr面，给我发了offer。</p><h2 id="翻车面试"><a href="#翻车面试" class="headerlink" title="翻车面试"></a>翻车面试</h2><p>如果各位以后有机会面试别人的话，希望对面试者多一些尊重吧。</p><ol><li>根据简历提前准备好，针对不同的level提不同的问题</li><li>不要问一些偏门的怪题</li><li>问题都是和面试职位相关的</li></ol><p>以下是一些翻车面试，有些公司真的把我心态搞崩了，导致后面的面试都没有发挥好。</p><h3 id="协能共创（自带简历）"><a href="#协能共创（自带简历）" class="headerlink" title="协能共创（自带简历）"></a>协能共创（自带简历）</h3><p>公司很小，面试官架子不小，面试全程非常不愉快，经常在我说到一半打断我，挺会嘲讽人的。面试的时候没有空间了，然后在外面靠近电梯的走廊面试的。</p><ol><li><p>因为我的项目是做人脸识别相机相关的，然后他问我图片处理到算法识别经历了什么过程，我是直接调算法，最多把jpg格式的图片转成bgr格式，或者图片有旋转的，通过exif修正旋转。但是我的回答好像不是他要的，然后就直接打断我，说我不知道就直接说不知道</p></li><li><p>rabbitmq用了哪些设计模式。发布订阅模式，还有其他的吗，我不知道，有知道的朋友可以告诉我一下</p></li><li><p>tcp&#x2F;ip协议</p></li><li><p>让你设计eureka，你怎么设计</p></li><li><p>乐观锁，悲观锁介绍一下</p><p>我：乐观锁实际上没有加锁</p><p>然后他就直接打断我了，发出一声嘲笑，问我：那你觉得什么是锁。</p></li><li><p>jvm参数调优怎么做的</p><p>我回答的是用的微服务部署，有的微服务访问很频繁的，就多给他设置一些内存，比如网关，一些不怎么用到的，可以设置稍微小一点。然后他说：你们这就是全凭感觉瞎设置呗</p></li></ol><p>哎，面完之后心情低落了很多，难受了一下午，开始质疑我自己。</p><h3 id="芯软科技（自带简历）"><a href="#芯软科技（自带简历）" class="headerlink" title="芯软科技（自带简历）"></a>芯软科技（自带简历）</h3><p>Boos直聘上标榜的上市公司，我去面试的时候顶多就看见5个人在公司，办公区域很空旷很安静。</p><ol><li><p>项目介绍</p></li><li><p>挑出你在项目里做的一个模块，谈谈这个模块的整个设计</p><p>这个我真不知道怎么回答，你说整个项目怎么设计的我还能画个架构图啥的给你瞅瞅，但是你这说到具体的模块设计，我不知道怎么说，最多给你说说数据库怎么设计的</p></li><li><p>项目并发情况</p><p>你这公司规模还问我这个，不合适吧</p></li><li><p>听你说对linux比较熟，环境搭建过吗（tomcat之类的），有没有遇到什么问题</p><p>搭环境都成为一个技术点来问了嘛，反正我现在用docker了。</p></li><li><p>nginx你们一般做什么，正向代理一般用来干啥</p></li><li><p>你会前端啊，说说vue用history打包之后，部署到服务器，nginx需要配置什么</p></li><li><p>说说你的优点</p></li><li><p>假如你最近加班很累（你已经很累了，要猝死了），但是项目比较急，你想请假怎么跟领导沟通。</p></li></ol><h3 id="广东世纪网通信设备股份有限公司（自带简历）"><a href="#广东世纪网通信设备股份有限公司（自带简历）" class="headerlink" title="广东世纪网通信设备股份有限公司（自带简历）"></a>广东世纪网通信设备股份有限公司（自带简历）</h3><p>第一次见到这样问问题的兄弟，挺有新鲜感的，其他公司翻来覆去的就是那几个问题。就是浪费了我的简历，打印一次一块钱，你还在我的简历上涂涂改改，我都不好意思要回简历了。</p><ol><li><p>java的序列化和反序列化什么含义  生成的serialVersionUID有什么用</p></li><li><p>yml文件里有一些属性，怎么获取这些属性</p></li><li><p>有一个枚举类，有很多value和code，然后yml中的一个属性对应这个code的值，怎么通过code的值获取到这个对应value</p><p>我想说循环判断，但是觉得没这么简单，所以沉默了半天。。。。</p><p>然后他说枚举类里面有一个方法valueOf</p><p>啊这，是他没表达清楚还是我理解错了？</p></li><li><p>final干嘛的，如果有一个final Stirng a &#x3D; “123”，后面还可以改变吗</p></li><li><p>一般io操作会捕获异常，然后在catch中释放资源，有更简单的办法吗。</p></li><li><p>有一个list，给它按照类里的一个属性排序</p></li><li><p>有个string数组，里面有些空的值，我想去除空的，怎么做</p></li><li><p>线上有个接口速度慢，但是本地测试环境和线上环境是一样的，数据量也是一样，接口速度却很快。怎么排查</p><p>不知道</p></li></ol><h3 id="中铁西南研究院"><a href="#中铁西南研究院" class="headerlink" title="中铁西南研究院"></a>中铁西南研究院</h3><p>面了这么多家，他家是第一次把我问的说不上话的，是电话面试。</p><ol><li><p>java的继承和多态讲讲，继承在项目里面是怎么使用的</p><p>在项目里怎么用我不知道怎么回答</p></li><li><p>说一下泛型</p><p>泛型提供了编译时类型安全检测机制，该机制可以在编译时期就检测到非法的类型，避免了运行时的类型转换异常。可能不是他想要的答案？我不知道他想问什么，他说算了下一个问题。</p></li><li><p>设计模式，说说简单工厂模式和抽象工厂模式怎么实现的，区别</p><p>我不知道怎么描述他的实现，不知道各位能不能口述出来。我语言表达能力真不行</p></li><li><p>java8新特性，讲讲用过stream里的哪些东西吧</p></li><li><p>mysql优化</p><p>说了一堆，然后说sql操作尽量使用预编译语句的时候，</p><p>他说：不是mybatis已经解决了嘛。。。哎</p></li><li><p>redis项目里怎么用</p><p>作用是支撑高性能高并发，然后在项目里具体怎么用的说了下。</p><p>他：一句话的事儿：不就是做缓存的嘛</p></li></ol><h3 id="通甲优博（自带简历）"><a href="#通甲优博（自带简历）" class="headerlink" title="通甲优博（自带简历）"></a>通甲优博（自带简历）</h3><p>坐车坐了两个小时，然后去面试先hr面，了解了基本的情况，然后没了。</p><p>我还是请假坐车2个小时过去的，hr没有提前了解我的基本信息就给我发面试邀请了吧，然后过来面试才觉得我不合适，所以直接就没有技术面试了。</p><h3 id="返空汇"><a href="#返空汇" class="headerlink" title="返空汇"></a>返空汇</h3><p>就是最开头说的那家，招聘网站上常年挂着招聘岗位，起码挂了快一年。过去先是做了一套笔试题，满分100，60分及格，没及格就没后续。</p><h3 id="慧友云商科技"><a href="#慧友云商科技" class="headerlink" title="慧友云商科技"></a>慧友云商科技</h3><p>同样是做了一套笔试题没有后续了。因为我发现有一个题有问题，然后留空白。</p><h3 id="国信杰云科技"><a href="#国信杰云科技" class="headerlink" title="国信杰云科技"></a>国信杰云科技</h3><p>前一天电话面试，自我介绍之后，问了一些技术问题，然后就让第二天到现场面试。</p><p>现场的话，整个公司人员不超过10个人，本来想走了的，想着来都来了，进去坐坐也无妨嘛。结果这是我做的最后悔的一个决定。</p><p>整个面试体验非常差，面试官线是让我自我介绍，我前一天已经在电话里做过自我介绍了，不知道这是什么意思，面试中途还接了几个电话。然后就问登录怎么设计的，前端密码怎么传输过去的，明文还是加密，我说前端加密没有必要也没意义，最多加上https，他说加上https没用，就是要加密传，我就在思考怎么回答他的时候，他说这是面试，请你尊重这次面试(说话语气带了点怒火，感觉要骂人了)，我也不能忍啊，直接起身就走了。</p><h2 id="常问问题"><a href="#常问问题" class="headerlink" title="常问问题"></a>常问问题</h2><h3 id="jvm调优"><a href="#jvm调优" class="headerlink" title="jvm调优"></a>jvm调优</h3><p>我觉得这不是一个中级开发工程师甚至高级开发工程师能做的出来的，但是面试就是要问。死记硬背考记忆力呗，只要记住其中几个常用的配置就行了。</p><h4 id="堆大小设置"><a href="#堆大小设置" class="headerlink" title="堆大小设置"></a>堆大小设置</h4><ol><li>-Xms:初始堆大小</li><li>-Xmx:最大堆大小</li><li>-XX:NewSize&#x3D;n:设置年轻代大小</li><li>-XX:NewRatio&#x3D;n:设置年轻代和年老代的比值。如:为3，表示年轻代与年老代比值为1：3，年轻代占整个年轻代年老代和的1&#x2F;4</li><li>-XX:SurvivorRatio&#x3D;n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如：3，表示Eden：Survivor&#x3D;3：2，一个Survivor区占整个年轻代的1&#x2F;5</li></ol><h4 id="收集器设置"><a href="#收集器设置" class="headerlink" title="收集器设置"></a>收集器设置</h4><ol><li>-XX:+UseSerialGC:设置串行收集器</li><li>-XX:+UseParallelGC:设置并行收集器</li><li>-XX:+UseParalledlOldGC:设置并行年老代收集器</li><li>-XX:+UseConcMarkSweepGC:设置并发收集器</li></ol><h4 id="调优总结"><a href="#调优总结" class="headerlink" title="调优总结"></a>调优总结</h4><p>年轻代选择</p><p>响应时间优先的应用：尽可能设大，直到接近系统的最低响应时间限制（根据实际情况选择）。在此种情况下，年轻代收集发生的频率也是最小的。同时，减少到达年老代的对象。<br>吞吐量优先的应用：尽可能的设置大，可能到达Gbit的程度。因为对响应时间没有要求，垃圾收集可以并行进行，一般适合8CPU以上的应用。</p><p>年老代选择</p><p>响应时间优先的应用：年老代使用并发收集器，所以其大小需要小心设置，一般要考虑并发会话率和会话持续时间等一些参数。如果堆设置小了，可以会造成内存碎片、高回收频率以及应用暂停而使用传统的标记清除方式；如果堆大了，则需要较长的收集时间。</p><h3 id="代码优化"><a href="#代码优化" class="headerlink" title="代码优化"></a>代码优化</h3><p>分为三个维度来谈，可以稍微拓展一下：</p><ol><li>整体框架的优化，根据模块（类）的职责做一些划分，尽量做到每个模块的接口清晰。然后就是善用六大设计原则和24种设计模式。</li><li>功能逻辑的优化，把内聚的逻辑独立成一个个函数，函数依赖的数据都通过参数传递，这样做到函数可重用，当上层结构做出改变的时候，修改的代码量不会太多。</li><li>性能优化，如果性能不敏感，代码也不是那么糟糕，那这个优化可以不用关注。</li></ol><h4 id="六大设计原则"><a href="#六大设计原则" class="headerlink" title="六大设计原则"></a>六大设计原则</h4><ul><li><p>单一职责原则</p><p>接口的单一职责，一个职责一个接口，我们对外公布的是接口，而不是实现类，所以不需要过度关心实现类是否遵循单一职责，生搬硬套单一职责会引起类的剧增，给维护带来非常多的麻烦</p></li><li><p>里式替换原则</p><p>只要父类能出现的地方我子类就可以出现，而且调用子类还不产生任何的错误或异常，调用者可能根本就不需要知道是父类还是子类。但是反过来就不成了，有子类出现的地方，父类未必就能适应。里式替换原则包含了四层意思：</p><ol><li>子类必须完全的实现父类的方法。我们经常写service会定义一个接口或者抽象类，然后编写实现，调用类则直接传入接口或抽象类，其实这里已经使用了里氏替换法则。</li><li>子类可以有自己的个性。子类当然可以有自己的行为和外观了，也就是方法和属性，在子类出现的地方，父类未必就可以胜任。父类向下转型可能会出现类型转换异常</li><li>覆盖或实现父类的方法时输入参数可以被放大。</li><li>覆盖或实现父类的方法是输出结果可以被缩小。</li></ol></li><li><p>依赖倒置原则</p><p>依赖倒置原则的本质就是通过抽象(接口或抽象类)使各个类或模块的实现彼此独立，不互相影响，实现模块间的松耦合。我们在项目中使用这个原则要遵循下面的规则：</p><p>每个类尽量都有接口或者抽象类，或者抽象类和接口两都具备</p><p>变量的表面类型尽量是接口或者抽象类</p><p>任何类都不应该从具体类派生</p><p>尽量不要覆写基类的方法</p></li><li><p>接口隔离原则</p><p>客户端不应该依赖它不需要的接口，客户端需要什么接口就提供什么接口，把不需要的接口剔除掉，对接口进行细化，保证其纯洁性。通俗的讲就是：建立单一接口，不要建立臃肿庞大的接口。</p></li><li><p>迪米特法则</p><p>一个对象应该对其他对象有最少的了解，通俗的讲，一个类对自己需要耦合&#x2F;调用的类应该知道的最少，这个类内部是怎么复杂、怎么纠缠不清都和我没关系。尽量不要对外公布太多public方法和非静态的public变量，尽量内敛，多使用private、package-private、protected等访问权限</p></li><li><p>开闭原则</p><p>类、方法对于扩展是开放的，对于修改是关闭的，在程序需要进行拓展的时候，不能去修改原来的代码。</p></li></ul><h4 id="24种设计模式"><a href="#24种设计模式" class="headerlink" title="24种设计模式"></a>24种设计模式</h4><p>单例模式</p><p>工厂模式</p><p>建造者模式</p><p>观察者模式</p><p>有观察者和被观察者，被观察者接口：增加观察者、删除观察者，通知观察者更新<br>观察者：被观察者发送消息了，我自己也要做些什么</p><p>责任链模式</p><p>适配器模式</p><h3 id="mysql的优化"><a href="#mysql的优化" class="headerlink" title="mysql的优化"></a>mysql的优化</h3><p>十家公司有九家会问，这个必须要记住。基本上只要记住我写的这些，面试基本上稳了：</p><ol><li>数据库表设计优化<ol><li>所有表必须使用Innodb存储引擎</li><li>字符集统一使用utf-8</li><li>字段加注释</li><li>控制单表数据量的大小，建议控制在500w内</li><li>冷热数据分离，减小表宽度</li><li>禁止存储图片，文件等二进制数据</li><li>三大范式</li></ol></li><li>数据库字段设计优化<ol><li>优先选择符合存储条件最小的数据类型，能用tinyint的就不用int，能用varchar的不用text</li><li>避免使用text，blob数据类型</li><li>尽可能把所有列定义为not null</li></ol></li><li>索引设计优化<ol><li>限制每张表的索引数量，单张表不超过5个</li><li>禁止给每一列都建立单独索引</li><li>每个表必须有一个主键</li><li>频繁查询的字段建立索引</li></ol></li><li>编写sql优化<ol><li>建议预编译语句进行数据库操作</li><li>避免数据类型的隐式转换</li><li>充分利用表上已经存在的索引</li><li>不要使用select * 查询，要使用select &lt;字段列表&gt; 查询</li><li>避免使用子查询，把子查询优化成join操作</li><li>避免使用join关联太多得表</li><li>对同一列进行or判断时，使用in代替or</li><li>where从句中禁止对列进行函数转换和计算</li><li>在明显不会有重复值时使用union all而不是union</li><li>拆分复杂的大SQL为多个小SQL</li><li>使用 group by 分组查询是，默认分组后，还会排序，可能会降低速度，在 group by 后面增加 order by null 就可以防止排序.</li></ol></li></ol><h3 id="jvm内存模型"><a href="#jvm内存模型" class="headerlink" title="jvm内存模型"></a>jvm内存模型</h3><p>必考题，抽时间看看深入理解jvm虚拟机吧。</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>面试和工作是两回事，面试没通过的人，工作能力不一定比你弱，面试通过的人，工作能力不一定比你强。所以面试没通过也不要灰心，回去好好总结复盘，不要因为失败就丧失斗志，加油！</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;从今年5月份就开始面试了，前前后后也有4个多月了吧，面试了几十家公司，其中有几十个人的小型创业公司，也有几百人的上市公司，踩了不少的坑，最后收到了宝能集团的offer，我也决定就去这家了。&lt;/p&gt;
&lt;p&gt;看过很多别人的面试分享经验，这里我也做一些面试的分享吧。&lt;/p&gt;
&lt;h</summary>
      
    
    
    
    <category term="面试" scheme="http://example.com/categories/%E9%9D%A2%E8%AF%95/"/>
    
    
  </entry>
  
  <entry>
    <title>SpringBoot之SpringApplication初始化</title>
    <link href="http://example.com/2021/05/10/SpringBoot%E4%B9%8BSpringApplication%E5%88%9D%E5%A7%8B%E5%8C%96/"/>
    <id>http://example.com/2021/05/10/SpringBoot%E4%B9%8BSpringApplication%E5%88%9D%E5%A7%8B%E5%8C%96/</id>
    <published>2021-05-10T02:34:25.000Z</published>
    <updated>2025-09-15T02:35:58.038Z</updated>
    
    <content type="html"><![CDATA[<h3 id="SpringApplication的初始化"><a href="#SpringApplication的初始化" class="headerlink" title="SpringApplication的初始化"></a>SpringApplication的初始化</h3><p>之前已经分析了引导类上的<code>@SpringBootApplication</code>注解，</p><p>接下来继续分析main方法，只调用了一句<code>SpringApplication.run(SpringbootApplication.class, args)</code>，就启动了web容器，我们看看run方法里面做了什么</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> ConfigurableApplicationContext <span class="title function_">run</span><span class="params">(Class&lt;?&gt;[] primarySources, String[] args)</span> &#123;</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">SpringApplication</span>(primarySources).run(args);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到，这里初始化了<code>SpringApplication</code>并且执行了它的run方法。</p><p>从<code>SpringApplication</code>的构造方法开始看，SpringApplication的构造方法主要做了四件事：</p><ol><li>配置源</li><li>推断web应用类型</li><li>应用上下文初始器和应用事件监听</li><li>推导引用主类</li></ol><p>源码如下:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SuppressWarnings(&#123; &quot;unchecked&quot;, &quot;rawtypes&quot; &#125;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="title function_">SpringApplication</span><span class="params">(ResourceLoader resourceLoader, Class&lt;?&gt;... primarySources)</span> &#123;</span><br><span class="line"><span class="built_in">this</span>.resourceLoader = resourceLoader;</span><br><span class="line">Assert.notNull(primarySources, <span class="string">&quot;PrimarySources must not be null&quot;</span>);</span><br><span class="line">        <span class="comment">//1.配置源</span></span><br><span class="line"><span class="built_in">this</span>.primarySources = <span class="keyword">new</span> <span class="title class_">LinkedHashSet</span>&lt;&gt;(Arrays.asList(primarySources));</span><br><span class="line">        <span class="comment">//2.推断web应用类型</span></span><br><span class="line"><span class="built_in">this</span>.webApplicationType = WebApplicationType.deduceFromClasspath();</span><br><span class="line">        <span class="comment">//3.应用上下文初始器和应用事件监听</span></span><br><span class="line">setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));</span><br><span class="line">setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));</span><br><span class="line">        <span class="comment">//4.推导引用主类</span></span><br><span class="line"><span class="built_in">this</span>.mainApplicationClass = deduceMainApplicationClass();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="配置源"><a href="#配置源" class="headerlink" title="配置源"></a>配置源</h3><p>配置springApplication的启动类，以<code>java的方式配置</code>我们是很熟悉的，直接用：<code>SpringApplication.run(MySpringBootApplication.class, args);</code>，其中<code>MySpringBootApplication.class</code>就是构造方法第二个参数的<code>primarySources</code></p><p>通过<code>XML的方式配置</code>：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">SpringApplication</span> <span class="variable">springApplication</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SpringApplication</span>();</span><br><span class="line">springApplication.setSources();</span><br><span class="line">springApplication.run(args);</span><br></pre></td></tr></table></figure><p>其实就是通过<code>setSources</code>方法，添加参数来配置，参数可以是类名，包名以及XML路径</p><p>那么Spring Boot是怎么加载这两种数据源的呢，我们可以看看<code>primarySources</code>这个set集合什么时候用到的：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> Set&lt;Object&gt; <span class="title function_">getAllSources</span><span class="params">()</span> &#123;</span><br><span class="line">Set&lt;Object&gt; allSources = <span class="keyword">new</span> <span class="title class_">LinkedHashSet</span>&lt;&gt;();</span><br><span class="line"><span class="keyword">if</span> (!CollectionUtils.isEmpty(<span class="built_in">this</span>.primarySources)) &#123;</span><br><span class="line">allSources.addAll(<span class="built_in">this</span>.primarySources); <span class="comment">// java方式配置的源</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> (!CollectionUtils.isEmpty(<span class="built_in">this</span>.sources)) &#123;</span><br><span class="line">allSources.addAll(<span class="built_in">this</span>.sources);  <span class="comment">//xml方式配置的源</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> Collections.unmodifiableSet(allSources);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">prepareContext</span><span class="params">(ConfigurableApplicationContext context, ConfigurableEnvironment environment,</span></span><br><span class="line"><span class="params">         //.....忽略代码</span></span><br><span class="line"><span class="params">                            </span></span><br><span class="line"><span class="params">// Load the sources</span></span><br><span class="line"><span class="params">Set&lt;Object&gt; sources = getAllSources()</span>;</span><br><span class="line">Assert.notEmpty(sources, <span class="string">&quot;Sources must not be empty&quot;</span>);</span><br><span class="line">load(context, sources.toArray(<span class="keyword">new</span> <span class="title class_">Object</span>[<span class="number">0</span>]));</span><br><span class="line">listeners.contextLoaded(context);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>prepareContext</code>这个方法在SpringApplication的run方法里面调用的，可以看到，获取到的<code>sources</code>又传给了<code>load</code>方法，继续跟进：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">load</span><span class="params">(ApplicationContext context, Object[] sources)</span> &#123;</span><br><span class="line"><span class="keyword">if</span> (logger.isDebugEnabled()) &#123;</span><br><span class="line">logger.debug(<span class="string">&quot;Loading source &quot;</span> + StringUtils.arrayToCommaDelimitedString(sources));</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">BeanDefinitionLoader</span> <span class="variable">loader</span> <span class="operator">=</span> createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);</span><br><span class="line"><span class="keyword">if</span> (<span class="built_in">this</span>.beanNameGenerator != <span class="literal">null</span>) &#123;</span><br><span class="line">loader.setBeanNameGenerator(<span class="built_in">this</span>.beanNameGenerator);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> (<span class="built_in">this</span>.resourceLoader != <span class="literal">null</span>) &#123;</span><br><span class="line">loader.setResourceLoader(<span class="built_in">this</span>.resourceLoader);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> (<span class="built_in">this</span>.environment != <span class="literal">null</span>) &#123;</span><br><span class="line">loader.setEnvironment(<span class="built_in">this</span>.environment);</span><br><span class="line">&#125;</span><br><span class="line">loader.load();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>嗯。。。继续跟进：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">protected</span> BeanDefinitionLoader <span class="title function_">createBeanDefinitionLoader</span><span class="params">(BeanDefinitionRegistry registry, Object[] sources)</span> &#123;</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">BeanDefinitionLoader</span>(registry, sources);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>好像到头了，进入<code>BeanDefinitionLoader</code>类的构造方法里看一下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) &#123;</span><br><span class="line">Assert.notNull(registry, <span class="string">&quot;Registry must not be null&quot;</span>);</span><br><span class="line">Assert.notEmpty(sources, <span class="string">&quot;Sources must not be empty&quot;</span>);</span><br><span class="line"><span class="built_in">this</span>.sources = sources;</span><br><span class="line"><span class="built_in">this</span>.annotatedReader = <span class="keyword">new</span> <span class="title class_">AnnotatedBeanDefinitionReader</span>(registry);</span><br><span class="line"><span class="built_in">this</span>.xmlReader = <span class="keyword">new</span> <span class="title class_">XmlBeanDefinitionReader</span>(registry);</span><br><span class="line"><span class="keyword">if</span> (isGroovyPresent()) &#123;</span><br><span class="line"><span class="built_in">this</span>.groovyReader = <span class="keyword">new</span> <span class="title class_">GroovyBeanDefinitionReader</span>(registry);</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">this</span>.scanner = <span class="keyword">new</span> <span class="title class_">ClassPathBeanDefinitionScanner</span>(registry);</span><br><span class="line"><span class="built_in">this</span>.scanner.addExcludeFilter(<span class="keyword">new</span> <span class="title class_">ClassExcludeFilter</span>(sources));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到，分别通过<code>AnnotatedBeanDefinitionReader</code>和<code>XmlBeanDefinitionReader</code>这两个类来读取并将配置源加载为Spring Bean。这也说明了，在springboot的配置阶段，可以有两个数据源。</p><h3 id="推断Web应用类型和主引导类"><a href="#推断Web应用类型和主引导类" class="headerlink" title="推断Web应用类型和主引导类"></a>推断Web应用类型和主引导类</h3><p>Spring Boot 不仅可以用作一个WEB工程，也可以作为非WEB工程来使用，那么springboot是如何推断应用类型的呢，答案是根据当前应用ClassPath中是否存在相关实现类来推断Web应用类型，类型在枚举类<code>WebApplicationType</code>中定义，总共有三种类型：</p><p>Web Reactive：枚举类型为<code>WebApplicationType.REACTIVE</code> ，对应的类路径：<code>org.springframework. web.reactive.DispatcherHandler</code></p><p>Web Servlet： 枚举类型为<code>WebApplicationType.SERVLET</code>，对应的类路径：<code>org.springframework.web.servlet.DispatcherServlet</code></p><p>非 Web：  枚举类型为<code>WebApplicationType.NONE</code></p><p>在WebApplicationType中通过<code>deduceFromClasspath</code>方法判断类型：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> WebApplicationType <span class="title function_">deduceFromClasspath</span><span class="params">()</span> &#123;</span><br><span class="line">         <span class="comment">//当类路径下存在Web Reactive 类路径并且不存在WebApplicationType.SERVLET类路径是才会启用</span></span><br><span class="line">         <span class="comment">//WEB Reactive容器</span></span><br><span class="line"><span class="keyword">if</span> (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, <span class="literal">null</span>) &amp;&amp; !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, <span class="literal">null</span>)</span><br><span class="line">&amp;&amp; !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, <span class="literal">null</span>)) &#123;</span><br><span class="line"><span class="keyword">return</span> WebApplicationType.REACTIVE;</span><br><span class="line">&#125;</span><br><span class="line">         <span class="comment">//当类路径中都不存在时，会启用非WEB类型</span></span><br><span class="line"><span class="keyword">for</span> (String className : SERVLET_INDICATOR_CLASSES) &#123;</span><br><span class="line"><span class="keyword">if</span> (!ClassUtils.isPresent(className, <span class="literal">null</span>)) &#123;</span><br><span class="line"><span class="keyword">return</span> WebApplicationType.NONE;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line">         <span class="comment">//默认启用tomcat容器</span></span><br><span class="line"><span class="keyword">return</span> WebApplicationType.SERVLET;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="应用上下文初始器和应用事件监听"><a href="#应用上下文初始器和应用事件监听" class="headerlink" title="应用上下文初始器和应用事件监听"></a>应用上下文初始器和应用事件监听</h3><p>调用了下面两个方法实现了初始化上下文和事件监听：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));</span><br><span class="line">setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));</span><br></pre></td></tr></table></figure><p>这两个的初始化都大同小异，所以只需要挑其中一个来看就行了，这里就看初始化应用上下文吧，首先通过<code>getSpringFactoriesInstances(ApplicationContextInitializer.class)</code>返回了一个集合，从这个方法的名字看大概可以猜到，是通过工厂模式初始化应用上下文，到底是不是这样，进去看一下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> &lt;T&gt; Collection&lt;T&gt; <span class="title function_">getSpringFactoriesInstances</span><span class="params">(Class&lt;T&gt; type, Class&lt;?&gt;[] parameterTypes, Object... args)</span> &#123;</span><br><span class="line"><span class="type">ClassLoader</span> <span class="variable">classLoader</span> <span class="operator">=</span> getClassLoader();</span><br><span class="line"><span class="comment">// Use names and ensure unique to protect against duplicates</span></span><br><span class="line">Set&lt;String&gt; names = <span class="keyword">new</span> <span class="title class_">LinkedHashSet</span>&lt;&gt;(SpringFactoriesLoader.loadFactoryNames(type, classLoader));</span><br><span class="line">List&lt;T&gt; instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);</span><br><span class="line">AnnotationAwareOrderComparator.sort(instances);</span><br><span class="line"><span class="keyword">return</span> instances;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>首先看一下通过<code>SpringFactoriesLoader.loadFactoryNames(type, classLoader)</code>这个方法，获取了一个set集合，传入了两个参数，第一个是<code>ApplicationContextInitializer.class</code>，第二个是类加载器，进入方法看一下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Load the fully qualified class names of factory implementations of the</span></span><br><span class="line"><span class="comment"> * given type from &#123;<span class="doctag">@value</span> #FACTORIES_RESOURCE_LOCATION&#125;, using the given</span></span><br><span class="line"><span class="comment"> * class loader.</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> factoryType the interface or abstract class representing the factory</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> classLoader the ClassLoader to use for loading resources; can be</span></span><br><span class="line"><span class="comment"> * &#123;<span class="doctag">@code</span> null&#125; to use the default</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@throws</span> IllegalArgumentException if an error occurs while loading factory names</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@see</span> #loadFactories</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> List&lt;String&gt; <span class="title function_">loadFactoryNames</span><span class="params">(Class&lt;?&gt; factoryType, <span class="meta">@Nullable</span> ClassLoader classLoader)</span> &#123;</span><br><span class="line"><span class="type">String</span> <span class="variable">factoryTypeName</span> <span class="operator">=</span> factoryType.getName();</span><br><span class="line"><span class="keyword">return</span> loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>通过注释我们可以了解到，这个方法是从<code>#FACTORIES_RESOURCE_LOCATION</code>中获取继承<code>ApplicationContextInitializer</code>的这个类的所有类的全限定类名，其中<code>#FACTORIES_RESOURCE_LOCATION </code>   就是一个字符串常量： <code> META-INF/spring.factories</code></p><p>这个spring.factories的内容如下：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"># Initializers</span><br><span class="line">org.springframework.context.ApplicationContextInitializer=\</span><br><span class="line">org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\</span><br><span class="line">org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener</span><br></pre></td></tr></table></figure><p>通过解析这个文件里的内容来获取全限定类名，最终返回一个string类型的集合，也就是我们最开始看到的那个<code>Set&lt;String&gt; names</code>;</p><p>然后传递给<code>createSpringFactoriesInstances</code>方法生成实例，最后再排序</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> &lt;T&gt; List&lt;T&gt; <span class="title function_">createSpringFactoriesInstances</span><span class="params">(Class&lt;T&gt; type, Class&lt;?&gt;[] parameterTypes,</span></span><br><span class="line"><span class="params">ClassLoader classLoader, Object[] args, Set&lt;String&gt; names)</span> &#123;</span><br><span class="line">List&lt;T&gt; instances = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;(names.size());</span><br><span class="line"><span class="keyword">for</span> (String name : names) &#123;</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">Class&lt;?&gt; instanceClass = ClassUtils.forName(name, classLoader);</span><br><span class="line">Assert.isAssignable(type, instanceClass);</span><br><span class="line">Constructor&lt;?&gt; constructor = instanceClass.getDeclaredConstructor(parameterTypes);</span><br><span class="line"><span class="type">T</span> <span class="variable">instance</span> <span class="operator">=</span> (T) BeanUtils.instantiateClass(constructor, args);</span><br><span class="line">instances.add(instance);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">catch</span> (Throwable ex) &#123;</span><br><span class="line"><span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">&quot;Cannot instantiate &quot;</span> + type + <span class="string">&quot; : &quot;</span> + name, ex);</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> instances;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="推导引用主类"><a href="#推导引用主类" class="headerlink" title="推导引用主类"></a>推导引用主类</h3><p>前面说过，我们一般写SpringBoot的启动类代码，会按照下面的方式：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MySpringBootApplication</span> &#123;</span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">      SpringApplication.run(MySpringBootApplication.class, args);</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们可以很明显的知道主类就是<code>MySpringBootApplication</code>，但是，SpringBoot有多种启动方式，而且SpringApplication.run第一个参数是可变的，也就是说，我可以在其他的类的main方法中调用SpringApplication.run方法启动<code>MySpringBootApplication</code>，那么那个类就是主类，而不是<code>MySpringBootApplication</code>：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">App</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        SpringApplication</span><br><span class="line">                .run(MySpringBootApplication.class,args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在SpringBoot的构造方法中调用了推导引用主类的方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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">private</span> Class&lt;?&gt; deduceMainApplicationClass() &#123;</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">StackTraceElement[] stackTrace = <span class="keyword">new</span> <span class="title class_">RuntimeException</span>().getStackTrace();</span><br><span class="line"><span class="keyword">for</span> (StackTraceElement stackTraceElement : stackTrace) &#123;</span><br><span class="line"><span class="keyword">if</span> (<span class="string">&quot;main&quot;</span>.equals(stackTraceElement.getMethodName())) &#123;</span><br><span class="line"><span class="keyword">return</span> Class.forName(stackTraceElement.getClassName());</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">catch</span> (ClassNotFoundException ex) &#123;</span><br><span class="line"><span class="comment">// Swallow and continue</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>debug看一下：</p><p><img src="https://user-gold-cdn.xitu.io/2020/4/23/171a59e6223c7c85?w=1869&h=835&f=png&s=150678"></p><p>可以看到的是，SpringBoot是通过异常堆栈来遍历判断出main方法，发现类名为<code>App</code>的类中方法名是main，从而推导出<code>App</code>就是主类。</p><p>所以SpringBoot的启动主类，是由它自己推导的，而不是我们传给它的。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h3 id=&quot;SpringApplication的初始化&quot;&gt;&lt;a href=&quot;#SpringApplication的初始化&quot; class=&quot;headerlink&quot; title=&quot;SpringApplication的初始化&quot;&gt;&lt;/a&gt;SpringApplication的初始化&lt;/</summary>
      
    
    
    
    <category term="java" scheme="http://example.com/categories/java/"/>
    
    
  </entry>
  
  <entry>
    <title>springboot自动装配</title>
    <link href="http://example.com/2021/04/15/springboot%E8%87%AA%E5%8A%A8%E8%A3%85%E9%85%8D/"/>
    <id>http://example.com/2021/04/15/springboot%E8%87%AA%E5%8A%A8%E8%A3%85%E9%85%8D/</id>
    <published>2021-04-15T02:33:21.000Z</published>
    <updated>2025-09-15T02:34:56.557Z</updated>
    
    <content type="html"><![CDATA[<h2 id="spring-boot-自动装配"><a href="#spring-boot-自动装配" class="headerlink" title="spring boot 自动装配"></a>spring boot 自动装配</h2><p>Spring Boot的很多特性都是基于spring framework来实现的，例如我们熟知的特性，也是它的核心特性：<strong>组件自动装配</strong>。它能够根据依赖的jar包自动配置Spring Boot的应用，例如： 如果类路径中存在DispatcherServlet类，就会自动配置springMvc相关的Bean。</p><p>而你需要的仅仅是编写一个启动类，加上<code>@SpringBootApplication</code>注解，执行它的main方法，接下来的就交给Sping Boot：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SpringbootApplication</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        SpringApplication.run(SpringbootApplication.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>为什么说Spring Boot很多特性是基于Spring framework实现的呢，在进入主题之前，需要先了解一下模式注解</p><h3 id="模式注解"><a href="#模式注解" class="headerlink" title="模式注解"></a>模式注解</h3><p>spring的模式注解：</p><p>一种用于声明在应用中扮演”组件”角色的注解,即标注了这个注解,表明一个类等在应用中扮演组件。比如<code>@Repository</code>标注在一个类上，说明这个类是扮演的仓储角色。</p><p>而<code>@Component</code>是作为通用模式组件，任何被<code>@Component</code>标注的组件均为组件扫描的候选对象。类似地，凡是被 <code>@Component</code> 标注的注解，如 <code>@Service</code> ，当任何组件标注它时，也被视作组件扫描的候选对象。</p><table><thead><tr><th>Spring Framework注解</th><th>场景说明</th><th>起始版本</th></tr></thead><tbody><tr><td>@Repository</td><td>数据仓储</td><td>2.0</td></tr><tr><td>@Component</td><td>通用组件</td><td>2.5</td></tr><tr><td>@Service</td><td>服务</td><td>2.5</td></tr><tr><td>@Controller</td><td>控制器</td><td>2.5</td></tr><tr><td>@Configuration</td><td>配置类</td><td>3.0</td></tr></tbody></table><h4 id="装配方式"><a href="#装配方式" class="headerlink" title="装配方式"></a>装配方式</h4><p>spring2.5之前，使用的是xml配置的方式实现：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">context:component-scan</span> <span class="attr">base-package</span>=<span class="string">&quot;com.wxpay.*&quot;</span>/&gt;</span></span><br></pre></td></tr></table></figure><p>spring3.1之后，可以使用注解实现：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@ComponentScan(&quot;com.wxpay.*&quot;)</span></span><br></pre></td></tr></table></figure><h4 id="自定义模式注解"><a href="#自定义模式注解" class="headerlink" title="自定义模式注解"></a>自定义模式注解</h4><p>模式注解的两种属性：</p><ol><li>派生性： <code>@component</code>,<code>@Repository</code>注解，都有value签名，保留了签名的一致性，这就是注解的派生性，换句话说，就是被<code>@component</code>标注的注解就具备了<code>@component</code>的功能</li><li>层次性： 声明二级注解<code>SecondLevelRepository</code>，我们的<code>SecondLevelRepository</code>派生于<code>FirstLevelRepository</code>,这就是层次性</li></ol><blockquote><p>严格上讲 注解是没有派生性和层次性的，之所以这样讲，是为了方便理解，因为在spring中的很多注解都是有着派生性和层次性的结构</p></blockquote><p>可能前面的说法有些不够明确，大家可能听的云里雾里的，接下来我们用一个例子来说明一下模式注解的特性。</p><p>首先我们先定义一个注解，使用<code>@Repository</code>标注：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Target(&#123;ElementType.TYPE&#125;)</span></span><br><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</span></span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="meta">@Repository</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> FirstLevelRepository &#123;</span><br><span class="line">    String <span class="title function_">value</span><span class="params">()</span> <span class="keyword">default</span> <span class="string">&quot;&quot;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后定义一个类，使用上面的注解标注：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@FirstLevelRepository(value = &quot;myFirstLevelRepository&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyFirstLevelRepository</span> &#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>最后再定义一个引导类，这儿使用了<code>@ComponentScan</code>扫描上面的类：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@ComponentScan(&quot;com.lingxiao.springboot.Repository&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RepositoryBootstrap</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">ConfigurableApplicationContext</span> <span class="variable">applicationContext</span> <span class="operator">=</span></span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">SpringApplicationBuilder</span>(RepositoryBootstrap.class).web(WebApplicationType.NONE).run(args);</span><br><span class="line">        <span class="type">MyFirstLevelRepository</span> <span class="variable">firstLevelRepository</span> <span class="operator">=</span> applicationContext</span><br><span class="line">                .getBean(<span class="string">&quot;myFirstLevelRepository&quot;</span>, MyFirstLevelRepository.class);</span><br><span class="line">        System.out.println(<span class="string">&quot;获取到的bean&quot;</span> + firstLevelRepository);</span><br><span class="line">        applicationContext.close();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>最终也是成功获取到了<code>MyFirstLevelRepository</code> ，这里可以看出，被<code>@Repository</code>标注的注解 <code>@FirstLevelRepository</code>，就像是继承关系，是具备<code>@Repository</code>的功能的。</p><p>然后我们再定义一个注解，使用<code>@FirstLevelRepository</code>来标注它：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Target(&#123;ElementType.TYPE&#125;)</span></span><br><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</span></span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="meta">@FirstLevelRepository</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> SeondLevelRepository &#123;</span><br><span class="line">    String <span class="title function_">value</span><span class="params">()</span> <span class="keyword">default</span> <span class="string">&quot;&quot;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>同样的方法，使用这个注解标注上面的类<code>MyFirstLevelRepository</code>，运行引导类<code>RepositoryBootstrap</code>，也是可以成功获取到bean的。这也印证了模式注解的<strong>派生性</strong>和<strong>层次性</strong>。</p><h4 id="SpringBootApplication也是模式注解"><a href="#SpringBootApplication也是模式注解" class="headerlink" title="@SpringBootApplication也是模式注解"></a>@SpringBootApplication也是模式注解</h4><p>好了，上面的例子已经说明模式注解的特性了，我们接下来可以看<code>@SpringBootApplication</code>这个注解了·，进去看看源码：</p><blockquote><p>源码版本为 spring-boot-2.2.5.RELEASE</p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Target(ElementType.TYPE)</span></span><br><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</span></span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="meta">@Inherited</span></span><br><span class="line"><span class="meta">@ComponentScan(excludeFilters = &#123; @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),</span></span><br><span class="line"><span class="meta">@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) &#125;)</span></span><br><span class="line"><span class="meta">@SpringBootConfiguration</span></span><br><span class="line"><span class="meta">@EnableAutoConfiguration</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> SpringBootApplication &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到<code>@SpringBootApplication</code>是一个组合注解</p><p><code>@ComponentScan</code>，配置自动扫包，没什么好说的</p><p>再点开<code>@SpringBootConfiguration</code>，我们可以看见：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> SpringBootConfiguration &#123;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>@SpringBootConfiguration</code>被<code>@Configuration</code>，说明<code>@SpringBootConfiguration</code>以及被它标注的<code>@SpringBootApplication</code>是模式注解</p><p>然后是<code>@EnableAutoConfiguration</code>，这是接下来要讲的<code>@Enable模块装配</code>，也是实现自动装配的核心</p><h3 id="Enable模块装配"><a href="#Enable模块装配" class="headerlink" title="@Enable模块装配"></a>@Enable模块装配</h3><p>使用@Enable模块装配，可以配置激活哪些模块</p><table><thead><tr><th>框架实现</th><th>注解模块</th><th></th></tr></thead><tbody><tr><td>Spring Framework</td><td>@EnableWebMvc</td><td></td></tr><tr><td></td><td>@EnableTransationManagement</td><td></td></tr><tr><td></td><td>@EnableCaching</td><td>Caching模块</td></tr><tr><td></td><td>@EnableMBeanExport</td><td>JMX模块</td></tr><tr><td></td><td>@EnableAsync</td><td>异步处理模块</td></tr><tr><td></td><td>@EnableWebFlux</td><td>Web Flux模块</td></tr><tr><td>Spring Boot</td><td>@EnableAutoConfiguration</td><td>自动装配模块</td></tr><tr><td></td><td>@EnableOAuth2Sao</td><td>OAuth2单点登录模块</td></tr></tbody></table><p>有两种方式实现@Enable</p><h4 id="基于注解驱动实现"><a href="#基于注解驱动实现" class="headerlink" title="基于注解驱动实现"></a>基于注解驱动实现</h4><p>先定义一个Bean：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HelloWorldConfiguration</span> &#123;</span><br><span class="line"> </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">helloWorld</span><span class="params">()</span> &#123; <span class="comment">// 方法名即 Bean 名称</span></span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;Hello,World&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后再定义一个注解，使用<code>@Import</code>导入刚刚定义的bean：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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">@Retention(RetentionPolicy.RUNTIME)</span></span><br><span class="line"><span class="meta">@Target(ElementType.TYPE)</span></span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="meta">@Import(HelloWorldConfiguration.class)</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> EnableHelloWorld &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>完成上面两步之后，我们只要用<code>@EnableHelloWorld</code>标注在某个类上时，这个Bean就会加载到Spring容器中。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@EnableHelloWorld</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">EnableHelloWorldBootstrap</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">ConfigurableApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SpringApplicationBuilder</span>(EnableHelloWorldBootstrap.class)</span><br><span class="line">                .web(WebApplicationType.NONE)</span><br><span class="line">                .run(args);</span><br><span class="line">        <span class="comment">// helloWorld Bean 是否存在</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">helloWorld</span> <span class="operator">=</span></span><br><span class="line">                context.getBean(<span class="string">&quot;helloWorld&quot;</span>, String.class);</span><br><span class="line">        System.out.println(<span class="string">&quot;获取到的bean: &quot;</span> + hello);</span><br><span class="line">        <span class="comment">// 关闭上下文</span></span><br><span class="line">        context.close();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>运行查看效果：</p><p><img src="https://user-gold-cdn.xitu.io/2020/4/24/171abb50c582f9cd?w=1787&h=414&f=png&s=54750"></p><p>可以看到这个bean确实已经加载到Spring容器中了。</p><h4 id="基于接口驱动实现"><a href="#基于接口驱动实现" class="headerlink" title="基于接口驱动实现"></a>基于接口驱动实现</h4><p>实现<code>ImportSelector</code>接口，实现它的<code>selectImports</code>方法，返回的是一个string类型的数组，数组里面存放的类名：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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_">HelloWorldImportSelector</span> <span class="keyword">implements</span> <span class="title class_">ImportSelector</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> String[] selectImports(AnnotationMetadata importingClassMetadata) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">String</span>[]&#123;HelloWorldConfiguration.class.getName()&#125;;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后将上面定义的注解改一下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Target(&#123;ElementType.TYPE&#125;)</span></span><br><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</span></span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="comment">//@Import(&#123;HelloWorldConfiguration.class&#125;)  enable注解驱动的方式</span></span><br><span class="line"><span class="meta">@Import(&#123;HelloWorldImportSelector.class&#125;)</span>  <span class="comment">//接口编程的方式</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> EnableHelloWorld &#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到，基于接口的方式实现更加灵活，我们可以在<code>selectImports</code>中做一些判断，根据需要返回不同的类名数组，然后再根据类名进行装配。</p><h4 id="Spring-Boot自动装配核心"><a href="#Spring-Boot自动装配核心" class="headerlink" title="Spring Boot自动装配核心"></a>Spring Boot自动装配核心</h4><p>我们再回头看一下<code>@EnableAutoConfiguration</code>这个注解：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Target(ElementType.TYPE)</span></span><br><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</span></span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="meta">@Inherited</span></span><br><span class="line"><span class="meta">@AutoConfigurationPackage</span></span><br><span class="line"><span class="meta">@Import(AutoConfigurationImportSelector.class)</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> EnableAutoConfiguration &#123;</span><br><span class="line"><span class="type">String</span> <span class="variable">ENABLED_OVERRIDE_PROPERTY</span> <span class="operator">=</span> <span class="string">&quot;spring.boot.enableautoconfiguration&quot;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>再点进<code>@AutoConfigurationPackage</code>看一下</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Target(ElementType.TYPE)</span></span><br><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</span></span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="meta">@Inherited</span></span><br><span class="line"><span class="meta">@Import(AutoConfigurationPackages.Registrar.class)</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> AutoConfigurationPackage &#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>使用<code>@Import</code>来给Spring容器中导入一个组件 ，这里导入的是Registrar.class</p><p><img src="https://user-gold-cdn.xitu.io/2020/4/25/171b0bbab40fcd1c?w=1983&h=992&f=png&s=155715"></p><p>运行springboot项目，debug看一下，可以发现metadata是被<code>@SpringBootApplication</code>标注的类，再看看<code>new PackageImport(metadata).getPackageName()</code>的值：</p><p><img src="https://user-gold-cdn.xitu.io/2020/4/25/171b0bbab5072737?w=865&h=909&f=png&s=30058"></p><p>这个值就是扫描的包路径，也就是说，默认扫描的包路径是引导类所在的包以及子包。</p><p>接着我们再看<code>EnableAutoConfiguration</code>也使用了<code>@import</code>，导入了 AutoConfigurationImportSelector.class，从这个类的名字我们就可以知道，它指定是实现了<code>ImportSelector</code>接口，重写了selectImports方法，所以我们直接看selectImports：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">public</span> String[] selectImports(AnnotationMetadata annotationMetadata) &#123;</span><br><span class="line"><span class="keyword">if</span> (!isEnabled(annotationMetadata)) &#123;</span><br><span class="line"><span class="keyword">return</span> NO_IMPORTS;</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">AutoConfigurationMetadata</span> <span class="variable">autoConfigurationMetadata</span> <span class="operator">=</span> AutoConfigurationMetadataLoader.loadMetadata(<span class="built_in">this</span>.beanClassLoader);</span><br><span class="line"><span class="type">AutoConfigurationEntry</span> <span class="variable">autoConfigurationEntry</span> <span class="operator">=</span> getAutoConfigurationEntry(autoConfigurationMetadata,annotationMetadata);</span><br><span class="line"><span class="keyword">return</span> tringUtils.toStringArray(autoConfigurationEntry.getConfigurations());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到返回结果是通过<code>autoConfigurationEntry</code>的<code>getConfigurations()</code>获取的，所以我们直接看<code>getAutoConfigurationEntry</code>方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">protected</span> AutoConfigurationEntry <span class="title function_">getAutoConfigurationEntry</span><span class="params">(AutoConfigurationMetadata autoConfigurationMetadata,</span></span><br><span class="line"><span class="params">AnnotationMetadata annotationMetadata)</span> &#123;</span><br><span class="line"><span class="keyword">if</span> (!isEnabled(annotationMetadata)) &#123;</span><br><span class="line"><span class="keyword">return</span> EMPTY_ENTRY;</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">AnnotationAttributes</span> <span class="variable">attributes</span> <span class="operator">=</span> getAttributes(annotationMetadata);</span><br><span class="line">List&lt;String&gt; configurations = getCandidateConfigurations(annotationMetadata, attributes);</span><br><span class="line">configurations = removeDuplicates(configurations);</span><br><span class="line">Set&lt;String&gt; exclusions = getExclusions(annotationMetadata, attributes);</span><br><span class="line">checkExcludedClasses(configurations, exclusions);</span><br><span class="line">configurations.removeAll(exclusions);</span><br><span class="line">configurations = filter(configurations, autoConfigurationMetadata);</span><br><span class="line">fireAutoConfigurationImportEvents(configurations, exclusions);</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">AutoConfigurationEntry</span>(configurations, exclusions);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>同理，这里我们进入<code>getCandidateConfigurations</code>方法看一下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">protected</span> List&lt;String&gt; <span class="title function_">getCandidateConfigurations</span><span class="params">(AnnotationMetadata metadata, AnnotationAttributes attributes)</span> &#123;</span><br><span class="line">List&lt;String&gt; configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),</span><br><span class="line">getBeanClassLoader());</span><br><span class="line">Assert.notEmpty(configurations, <span class="string">&quot;No auto configuration classes found in META-INF/spring.factories. If you &quot;</span></span><br><span class="line">+ <span class="string">&quot;are using a custom packaging, make sure that file is correct.&quot;</span>);</span><br><span class="line"><span class="keyword">return</span> configurations;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>想必英文不太好的童鞋从这里面也能看出一些端倪，没错，最终加载的配置类会从<code>META-INF/spring.factories</code>中获取，也就是说，Spring Boot在启动的时候会从类路径下的<code>META-INF/spring.factories</code>文件中将指定的值作为配置类导入到容器中，也就实现了自动配置。</p><h3 id="条件装配"><a href="#条件装配" class="headerlink" title="条件装配"></a>条件装配</h3><p>条件装配注解有两种：</p><table><thead><tr><th>Spring注解</th><th>场景说明</th><th>起始版本</th></tr></thead><tbody><tr><td>@Profile</td><td>配置化条件装配</td><td>3.1</td></tr><tr><td>@Conditional</td><td>编程条件装配</td><td>4.0</td></tr></tbody></table><blockquote><p>在4.0之后@profile也变成了@Conditional来实现</p></blockquote><p>应用场景：一般用于生产环境和开发环境之间的切换，就是在类或者方法上添加注解并设置环境标识比如<code>java7</code>、<code>java8</code>，我们以一个简单的多整数求和来演示如何使用</p><h4 id="Profile实现条件装配"><a href="#Profile实现条件装配" class="headerlink" title="@Profile实现条件装配"></a>@Profile实现条件装配</h4><p>首先定义一个接口：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">CalCulateService</span> &#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 整数求和</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> args</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">    Integer <span class="title function_">sum</span><span class="params">(Integer... args)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后分别实现一个JDK7和JDK8的求和方法。</p><p>首先是JDK7的实现：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * java7的方式实现求和</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Profile(&quot;Java7&quot;)</span></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Java7CalCulateServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">CalCulateService</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Integer <span class="title function_">sum</span><span class="params">(Integer... args)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;java7的方式求和&quot;</span>);</span><br><span class="line">        <span class="type">int</span> <span class="variable">sum</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; args.length; i++) &#123;</span><br><span class="line">            sum+=args[i];</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> sum;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后是JDK8的实现：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">@Profile(&quot;Java8&quot;)</span></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Java8CalCulateServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">CalCulateService</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Integer <span class="title function_">sum</span><span class="params">(Integer... args)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;java8的方式求和&quot;</span>);</span><br><span class="line">        <span class="type">int</span> <span class="variable">sum</span> <span class="operator">=</span> Stream.of(args).reduce(<span class="number">0</span>,Integer::sum);</span><br><span class="line">        <span class="keyword">return</span> sum;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后创建一个启动类，在启动容器的时候，使用<code>.profiles(&quot;Java8&quot;)</code>来指定使用哪个版本来计算：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication(scanBasePackages = &quot;com.lingxiao.springboot.service&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CalCulateServiceBootstrap</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">ConfigurableApplicationContext</span> <span class="variable">applicationContext</span> <span class="operator">=</span></span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">SpringApplicationBuilder</span>(CalCulateServiceBootstrap.class)</span><br><span class="line">                        .web(WebApplicationType.NONE)</span><br><span class="line">                        .profiles(<span class="string">&quot;Java8&quot;</span>)</span><br><span class="line">                        .run(args);</span><br><span class="line">        <span class="type">CalCulateService</span> <span class="variable">calCulateService</span> <span class="operator">=</span> applicationContext</span><br><span class="line">                .getBean(CalCulateService.class);</span><br><span class="line">        System.out.println(<span class="string">&quot;求和: &quot;</span> + calCulateService.sum(<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>));</span><br><span class="line">        applicationContext.close();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>运行结果：</p><p><img src="https://user-gold-cdn.xitu.io/2020/4/24/171abb50c89f2ca9?w=1696&h=471&f=png&s=64898"></p><p>可以看到确实是java8的方式实现的求和</p><h4 id="Conditional实现条件装配"><a href="#Conditional实现条件装配" class="headerlink" title="@Conditional实现条件装配"></a>@Conditional实现条件装配</h4><p>在创建bean时，增加一系列条件限制，只有当所有指定的条件都满足是，组件才可以注册。在使用<code>@Conditional</code>时，进行条件判断的类必须实现<code>Condition</code>接口</p><p>现在我们自定义一个条件装配，创建一个注解：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 编程方式实现条件装配</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</span></span><br><span class="line"><span class="meta">@Target(&#123;ElementType.TYPE,ElementType.METHOD&#125;)</span></span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="meta">@Conditional(OnSystemPropertyCondition.class)</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> ConditionalOnSystemProperty &#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * java系统属性名称</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_">name</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">     * java系统属性值</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    String <span class="title function_">value</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在这个注解中定义了两个属性<code>name</code>和<code>value</code>，这两个属性在实现条件判断的<code>OnSystemPropertyCondition</code>中会用到。</p><p>创建条件判断类：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 编程方式实现条件装配</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OnSystemPropertyCondition</span> <span class="keyword">implements</span> <span class="title class_">Condition</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> context</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> metadata 源信息</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">public</span> <span class="type">boolean</span> <span class="title function_">matches</span><span class="params">(ConditionContext context, AnnotatedTypeMetadata metadata)</span> &#123;</span><br><span class="line">        MultiValueMap&lt;String, Object&gt; attributes = metadata.getAllAnnotationAttributes(ConditionalOnSystemProperty.class.getName());</span><br><span class="line">        List&lt;Object&gt; nameObj = attributes.get(<span class="string">&quot;name&quot;</span>);</span><br><span class="line">        <span class="type">String</span> <span class="variable">name</span> <span class="operator">=</span> <span class="string">&quot;&quot;</span>;</span><br><span class="line">        <span class="keyword">if</span> (!CollectionUtils.isEmpty(nameObj)) &#123;</span><br><span class="line">            name = String.valueOf(attributes.get(<span class="string">&quot;name&quot;</span>).get(<span class="number">0</span>));</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">String</span> <span class="variable">value</span> <span class="operator">=</span> <span class="string">&quot;&quot;</span>;</span><br><span class="line">        <span class="keyword">if</span> (!CollectionUtils.isEmpty(nameObj)) &#123;</span><br><span class="line">            value = String.valueOf(attributes.get(<span class="string">&quot;value&quot;</span>).get(<span class="number">0</span>));</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">String</span> <span class="variable">property</span> <span class="operator">=</span> System.getProperty(name);</span><br><span class="line">        <span class="keyword">return</span> value.equals(property);  <span class="comment">//条件满足才会返回装配</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>使用<code>metadata.getAllAnnotationAttributes</code>获取注解中的属性值，然后判断是否满足条件，满足条件之后才会装配。</p><p>创建引导类：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ConditionalOnSystemPropertyBootstrap</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="meta">@ConditionalOnSystemProperty(name = &quot;java.vm.specification.version&quot;,value = &quot;1.8&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">helloWorld</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;hello world&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">ConfigurableApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SpringApplicationBuilder</span>(ConditionalOnSystemPropertyBootstrap.class)</span><br><span class="line">                .web(WebApplicationType.NONE)</span><br><span class="line">                .run(args);</span><br><span class="line">        <span class="type">String</span> <span class="variable">helloWorld</span> <span class="operator">=</span> context.getBean(<span class="string">&quot;helloWorld&quot;</span>, String.class);</span><br><span class="line">        System.out.println(<span class="string">&quot;bean name:  &quot;</span>+helloWorld);</span><br><span class="line">        context.close();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在这个引导类中我们申明了一个helloWorld的bean，在这个方法上添加了条件装配注释，其中<code>java.vm.specification.version</code>是获取的java虚拟机版本，如果满足虚拟机为1.8的条件，这个bean就会被加载到spring容器中</p><p>当前版本为1.8时，运行代码：</p><p><img src="https://user-gold-cdn.xitu.io/2020/4/24/171abb50c582f9cd?w=1787&h=414&f=png&s=54750"></p><p>正常加载没有问题，当我们切换版本之后，再次运行：</p><p><img src="https://user-gold-cdn.xitu.io/2020/4/24/171abb50c95036a6?w=1724&h=459&f=png&s=126495"></p><p>发现报错了，spring找不到这个bean，也就是说bean没有满足被加载的条件</p><h3 id="实现自动装配"><a href="#实现自动装配" class="headerlink" title="实现自动装配"></a>实现自动装配</h3><p>罗里吧嗦了这么多，我们看看如何实现Spring Boot的自动装配</p><p>而Spring Boot的自动装配是结合了上面所说的技术，所以我们可以基于Spring Framework的知识点实现自动装配，步骤如下：</p><ul><li>实现需要装配的类</li><li>Spring工厂加载，配置需要装配的类<ul><li>实现类 ：SpringFactoriesLoader</li><li>配置资源：META-INF&#x2F;spring.factories</li></ul></li><li>激活自动装配-@EnableAutoConfiguration</li></ul><p>首先创建一个类：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 自动装配</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Configuration</span>   <span class="comment">//模式注解</span></span><br><span class="line"><span class="meta">@EnableHelloWorld</span>  <span class="comment">//Enable模块装配</span></span><br><span class="line"><span class="meta">@ConditionalOnSystemProperty(name = &quot;java.vm.specification.version&quot;,value = &quot;1.8&quot;)</span> <span class="comment">//条件装配</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HelloWorldAutoConfiguration</span> &#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到，修饰的三个注解都是基于Spring Framework的，现在只需要配置spring.factories文件即可，我们在resouries目录下新建文件：</p><p><img src="https://user-gold-cdn.xitu.io/2020/4/25/171b0bbab5f426a8?w=1866&h=1250&f=png&s=126863"></p><p>内容为key-value的格式，key为<code>@EnableAutoConfiguration</code>，value为自动装配类的包路径：</p><figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 自动装配</span></span><br><span class="line"><span class="attr">org.springframework.boot.autoconfigure.EnableAutoConfiguration</span>=<span class="string">\</span></span><br><span class="line"><span class="string">com.lingxiao.springboot.configuration.HelloWorldAutoConfiguration</span></span><br></pre></td></tr></table></figure><p>然后再创建一个启动类：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@EnableAutoConfiguration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">EnableAutoConfigurationBootstrap</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">ConfigurableApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SpringApplicationBuilder</span>(EnableAutoConfigurationBootstrap.class)</span><br><span class="line">                .web(WebApplicationType.NONE)</span><br><span class="line">                .run(args);</span><br><span class="line">        <span class="type">String</span> <span class="variable">hello</span> <span class="operator">=</span> context</span><br><span class="line">                .getBean(<span class="string">&quot;helloWorld&quot;</span>, String.class);</span><br><span class="line">        System.out.println(<span class="string">&quot;获取到的bean: &quot;</span> + hello);</span><br><span class="line">        context.close();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>运行结果也是能正确获取到bean。这里梳理一下整个流程：</p><ol><li>使用<code>@EnableAutoConfiguration</code>激活自动装配，Spring Boot会去<code>spring.factories</code>文件中解析需要自动装配的类 <code>HelloWorldAutoConfiguration</code></li><li>装配<code>HelloWorldAutoConfiguration</code>的时候会去判断是否满足装配要求，这里是jdk1.8的环境，所以是满足要求的</li><li><code>HelloWorldAutoConfiguration</code>是被<code>@EnableHelloWorld</code>标注了，<code>@EnableHelloWorld</code>通过<code>@Import(&#123;HelloWorldImportSelector.class&#125;)</code>的方式引入了<code>HelloWorldImportSelector</code></li><li>在<code>HelloWorldImportSelector</code>的<code>selectImports</code>方法中返回了<code>HelloWorldConfiguration</code>的类名</li><li>在<code>HelloWorldConfiguration</code>中加载<code>helloWorld</code></li></ol><p>源码已上传至gitee：<a href="https://gitee.com/lingxiao12345/SpringBoot.git">点击访问</a></p><p>参考：</p><p><a href="https://blog.csdn.net/Dongguabai/article/details/80865599">SpringBoot自动装配原理分析</a></p><p><a href="https://coding.imooc.com/class/chapter/252.html#Anchor">慕课网：Spring Boot2.0深度实践之核心技术篇</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;spring-boot-自动装配&quot;&gt;&lt;a href=&quot;#spring-boot-自动装配&quot; class=&quot;headerlink&quot; title=&quot;spring boot 自动装配&quot;&gt;&lt;/a&gt;spring boot 自动装配&lt;/h2&gt;&lt;p&gt;Spring Boot的很多</summary>
      
    
    
    
    <category term="java" scheme="http://example.com/categories/java/"/>
    
    
    <category term="技术分享" scheme="http://example.com/tags/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/"/>
    
  </entry>
  
  <entry>
    <title>按序打印</title>
    <link href="http://example.com/2021/04/10/%E6%8C%89%E5%BA%8F%E6%89%93%E5%8D%B0/"/>
    <id>http://example.com/2021/04/10/%E6%8C%89%E5%BA%8F%E6%89%93%E5%8D%B0/</id>
    <published>2021-04-10T02:32:01.000Z</published>
    <updated>2025-09-15T02:32:33.708Z</updated>
    
    <content type="html"><![CDATA[<p>力扣上的一道题，三个线程按照顺序分别打印出来one、tow、three：</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Foot</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">val</span> flag:AtomicInteger = AtomicInteger(<span class="number">0</span>)</span><br><span class="line">    <span class="meta">@Throws(InterruptedException::class)</span></span><br><span class="line">    <span class="function"><span class="keyword">fun</span> <span class="title">first</span><span class="params">(printFirst: <span class="type">Runnable</span>)</span></span> &#123;</span><br><span class="line">        <span class="comment">// printFirst.run() outputs &quot;first&quot;. Do not change or remove this line.</span></span><br><span class="line">        printFirst.run()</span><br><span class="line">        flag.incrementAndGet()</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Throws(InterruptedException::class)</span></span><br><span class="line">    <span class="function"><span class="keyword">fun</span> <span class="title">second</span><span class="params">(printSecond: <span class="type">Runnable</span>)</span></span> &#123;</span><br><span class="line">        <span class="keyword">while</span> (flag.<span class="keyword">get</span>() != <span class="number">1</span>) &#123;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// printSecond.run() outputs &quot;second&quot;. Do not change or remove this line.</span></span><br><span class="line">        printSecond.run()</span><br><span class="line">        flag.incrementAndGet()</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Throws(InterruptedException::class)</span></span><br><span class="line">    <span class="function"><span class="keyword">fun</span> <span class="title">third</span><span class="params">(printThird: <span class="type">Runnable</span>)</span></span> &#123;</span><br><span class="line">        <span class="keyword">while</span> (flag.<span class="keyword">get</span>() != <span class="number">2</span>) &#123;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// printThird.run() outputs &quot;third&quot;. Do not change or remove this line.</span></span><br><span class="line">        printThird.run()</span><br><span class="line">        flag.incrementAndGet()</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">internal</span> <span class="keyword">class</span> <span class="title class_">PrintRunnable</span>(<span class="keyword">private</span> <span class="keyword">val</span> text: String) : Runnable &#123;</span><br><span class="line">        <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">run</span><span class="params">()</span></span> &#123;</span><br><span class="line">            println(text)</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">fun</span> <span class="title">main</span><span class="params">()</span></span>&#123;</span><br><span class="line">    <span class="keyword">val</span> foot = Foot()</span><br><span class="line">    thread &#123;</span><br><span class="line">        <span class="keyword">val</span> tow = Foot.PrintRunnable(<span class="string">&quot;tow&quot;</span>)</span><br><span class="line">        foot.second(tow)</span><br><span class="line">    &#125;</span><br><span class="line">    thread &#123;</span><br><span class="line">        <span class="keyword">val</span> three = Foot.PrintRunnable(<span class="string">&quot;three&quot;</span>)</span><br><span class="line">        foot.third(three)</span><br><span class="line">    &#125;</span><br><span class="line">    thread &#123;</span><br><span class="line">        <span class="keyword">val</span> one = Foot.PrintRunnable(<span class="string">&quot;one&quot;</span>)</span><br><span class="line">        foot.first(one)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;力扣上的一道题，三个线程按照顺序分别打印出来one、tow、three：&lt;/p&gt;
&lt;figure class=&quot;highlight kotlin&quot;&gt;&lt;table&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;</summary>
      
    
    
    
    <category term="算法" scheme="http://example.com/categories/%E7%AE%97%E6%B3%95/"/>
    
    
  </entry>
  
  <entry>
    <title>深入理解多线程之一文读懂锁</title>
    <link href="http://example.com/2021/04/09/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E5%A4%9A%E7%BA%BF%E7%A8%8B%E4%B9%8B%E4%B8%80%E6%96%87%E8%AF%BB%E6%87%82%E9%94%81/"/>
    <id>http://example.com/2021/04/09/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E5%A4%9A%E7%BA%BF%E7%A8%8B%E4%B9%8B%E4%B8%80%E6%96%87%E8%AF%BB%E6%87%82%E9%94%81/</id>
    <published>2021-04-09T02:29:32.000Z</published>
    <updated>2025-09-15T02:31:12.471Z</updated>
    
    <content type="html"><![CDATA[<h3 id="什么是锁"><a href="#什么是锁" class="headerlink" title="什么是锁"></a>什么是锁</h3><p>锁的概念源于生活，每家每户都有一把锁，只有持有钥匙才能打开锁进入房间。对于程序来说，就是防止其他线程进入，先到的线程进入房间锁上门，后到的线程看到上锁了，就在门口排队等待。</p><p><img src="https://blog-1252348761.cos.ap-chengdu.myqcloud.com/image/queue_up.jpg"></p><h3 id="为什么需要锁"><a href="#为什么需要锁" class="headerlink" title="为什么需要锁"></a>为什么需要锁</h3><p>多线程并发的场景下，防止多个线程同时读写某一块内存区域，造成数据的不一致性。</p><h3 id="java中的锁"><a href="#java中的锁" class="headerlink" title="java中的锁"></a>java中的锁</h3><p>首先说明：Java多线程的锁都是基于对象的，Java中的每一个对象都可以作为一个锁。</p><p>java中的锁分为内置锁（synchronized）和显式锁（ReentrantLock），其中内置锁是JVM提供的最便捷的线程同步工具，在代码块或方法声明上添加synchronized关键字即可使用内置锁。使用内置锁能够简化并发模型；随着JVM的升级，几乎不需要修改代码，就可以直接享受JVM在内置锁上的优化成果。<br>既然内置锁已经这么好了，那为什么还需要显式锁呢？</p><ol><li><code>synchronized</code>没有等待超时时间，获取不到锁会无限等待；</li><li>无法中断，如果我们想外部线程发送中断信号的方式停止当前线程获取锁，不得行；</li><li>我们想为锁维持多个等待队列，比如一个生产者队列，一个消费者队列，一边提高锁的效率。</li><li><code>synchronized</code>只能是非公平锁，而<code>ReentrantLock</code>可以指定公平还是非公平</li></ol><p>接下来分别介绍两种锁。</p><h4 id="显式锁"><a href="#显式锁" class="headerlink" title="显式锁"></a>显式锁</h4><p>使用方式很简单，下面是一个ReentrantLock的示例：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><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">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">InstanceCallable</span> <span class="keyword">implements</span> <span class="title class_">Callable</span>&lt;Integer&gt; &#123;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">int</span> <span class="variable">sharedNum</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">private</span> <span class="type">Lock</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReentrantLock</span>();</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">increase</span><span class="params">()</span>&#123;</span><br><span class="line">            lock.lock();</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">100</span>; i++) &#123;</span><br><span class="line">                    sharedNum++;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;<span class="keyword">catch</span> (Exception e)&#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;<span class="keyword">finally</span> &#123;</span><br><span class="line">                lock.unlock();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> Integer <span class="title function_">call</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">            increase();</span><br><span class="line">            <span class="keyword">return</span> sharedNum;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">ExecutorService</span> <span class="variable">executorService</span> <span class="operator">=</span> Executors.newFixedThreadPool(<span class="number">2</span>);</span><br><span class="line">        <span class="type">InstanceCallable</span> <span class="variable">instanceCallable</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">InstanceCallable</span>();</span><br><span class="line">        List&lt;Future&lt;Integer&gt;&gt; futures = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">2</span>; i++) &#123;</span><br><span class="line">            futures.add(executorService.submit(instanceCallable));</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">for</span> (Future&lt;Integer&gt; future : futures) &#123;</span><br><span class="line">                <span class="type">Integer</span> <span class="variable">result</span> <span class="operator">=</span> future.get();</span><br><span class="line">                System.out.println(<span class="string">&quot;结果&quot;</span>+result);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (ExecutionException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>打印结果：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">结果100</span><br><span class="line">结果200</span><br></pre></td></tr></table></figure><p>这里使用了<code>Future</code> 来获取线程执行的结果， 要注意的地方是需要吧<code>unlock()</code>方法放到finally里面，这样无论你的代码是否发生异常，锁都会释放掉，不然可能造成锁永远都得不到释放。</p><h4 id="内置锁"><a href="#内置锁" class="headerlink" title="内置锁"></a>内置锁</h4><p>synchronize内置锁主要有以下三种使用方式：</p><ol><li>修饰实例方法，获取的是当前实例的锁</li><li>修饰静态方法，获取的是当前class类的锁</li><li>修饰代码块，获取的锁对象是synchronized(obj)中的这个obj</li></ol><h5 id="1-修饰实例方法"><a href="#1-修饰实例方法" class="headerlink" title="1. 修饰实例方法"></a>1. 修饰实例方法</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">InstanceCallable</span> <span class="keyword">implements</span> <span class="title class_">Callable</span>&lt;Integer&gt; &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">int</span> <span class="variable">sharedNum</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title function_">increase</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">100</span>; i++) &#123;</span><br><span class="line">            sharedNum++;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Integer <span class="title function_">call</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        increase();</span><br><span class="line">        <span class="keyword">return</span> sharedNum;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="type">ExecutorService</span> <span class="variable">executorService</span> <span class="operator">=</span> Executors.newFixedThreadPool(<span class="number">2</span>);</span><br><span class="line">    <span class="type">InstanceCallable</span> <span class="variable">instanceCallable</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">InstanceCallable</span>();</span><br><span class="line">    List&lt;Future&lt;Integer&gt;&gt; futures = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">2</span>; i++) &#123;</span><br><span class="line">        futures.add(executorService.submit(instanceCallable));</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (Future&lt;Integer&gt; future : futures) &#123;</span><br><span class="line">            <span class="type">Integer</span> <span class="variable">result</span> <span class="operator">=</span> future.get();</span><br><span class="line">            System.out.println(<span class="string">&quot;结果&quot;</span>+result);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125; <span class="keyword">catch</span> (ExecutionException e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>打印结果：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">结果100</span><br><span class="line">结果200</span><br></pre></td></tr></table></figure><h5 id="2-修饰静态方法"><a href="#2-修饰静态方法" class="headerlink" title="2. 修饰静态方法"></a>2. 修饰静态方法</h5><p>将上面的代码稍微改一下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">InstanceCallable</span> <span class="keyword">implements</span> <span class="title class_">Callable</span>&lt;Integer&gt; &#123;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">int</span> <span class="variable">sharedNum</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title function_">increase</span><span class="params">()</span>&#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">100</span>; i++) &#123;</span><br><span class="line">                sharedNum++;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> Integer <span class="title function_">call</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">            increase();</span><br><span class="line">            <span class="keyword">return</span> sharedNum;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">ExecutorService</span> <span class="variable">executorService</span> <span class="operator">=</span> Executors.newFixedThreadPool(<span class="number">2</span>);</span><br><span class="line">        <span class="comment">//InstanceCallable instanceCallable = new InstanceCallable();</span></span><br><span class="line">        List&lt;Future&lt;Integer&gt;&gt; futures = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">2</span>; i++) &#123;</span><br><span class="line">            futures.add(executorService.submit(<span class="keyword">new</span> <span class="title class_">InstanceCallable</span>()));</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">for</span> (Future&lt;Integer&gt; future : futures) &#123;</span><br><span class="line">                <span class="type">Integer</span> <span class="variable">result</span> <span class="operator">=</span> future.get();</span><br><span class="line">                System.out.println(<span class="string">&quot;结果&quot;</span>+result);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (ExecutionException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br></pre></td></tr></table></figure><p>这个时候你会发现synchronized修饰实例方法不管用了，因为我是在for循环里new的callable对象，这个时候获取的锁是当前对象的锁，两个不同的线程获取的锁是不同的，所以修改共享变量的时候相当于没有加锁，会存在安全问题。<br>打印结果：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">结果103</span><br><span class="line">结果181</span><br></pre></td></tr></table></figure><p>这个时候只需要给increase方法加上static修饰符就行了：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">private</span> <span class="keyword">static</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title function_">increase</span><span class="params">()</span>&#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">100</span>; i++) &#123;</span><br><span class="line">                sharedNum++;</span><br><span class="line">            &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>synchronized作用于静态方法时，这个锁就是当前类的class对象锁。由于静态成员不专属于任何一个实例对象，是类成员，因此通过class对象锁可以控制静态成员的并发操作。</p><h5 id="3-修饰代码块"><a href="#3-修饰代码块" class="headerlink" title="3. 修饰代码块"></a>3. 修饰代码块</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><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="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">InstanceCallable</span> <span class="keyword">implements</span> <span class="title class_">Callable</span>&lt;Integer&gt; &#123;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">int</span> <span class="variable">sharedNum</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">increase</span><span class="params">()</span>&#123;</span><br><span class="line">            <span class="keyword">synchronized</span> (<span class="built_in">this</span>)&#123;</span><br><span class="line">                <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">100</span>; i++) &#123;</span><br><span class="line">                    sharedNum++;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> Integer <span class="title function_">call</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">            increase();</span><br><span class="line">            <span class="keyword">return</span> sharedNum;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">ExecutorService</span> <span class="variable">executorService</span> <span class="operator">=</span> Executors.newFixedThreadPool(<span class="number">2</span>);</span><br><span class="line">        <span class="comment">//InstanceCallable instanceCallable = new InstanceCallable();</span></span><br><span class="line">        List&lt;Future&lt;Integer&gt;&gt; futures = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">2</span>; i++) &#123;</span><br><span class="line">            futures.add(executorService.submit(<span class="keyword">new</span> <span class="title class_">InstanceCallable</span>()));</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">for</span> (Future&lt;Integer&gt; future : futures) &#123;</span><br><span class="line">                <span class="type">Integer</span> <span class="variable">result</span> <span class="operator">=</span> future.get();</span><br><span class="line">                System.out.println(<span class="string">&quot;结果&quot;</span>+result);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (ExecutionException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>当前实例对象就是锁对象，每次当线程进入synchronized包裹的代码块时就会要求当前线程持实例对象锁，如果当前有其他线程正持有该对象锁，那么新到的线程就必须等待，这样也就保证了每次只有一个线程执行代码块。</p><h3 id="锁的优化"><a href="#锁的优化" class="headerlink" title="锁的优化"></a>锁的优化</h3><p>JDK1.5中，<code>synchronized</code>的性能非常低效。因为这是一个重量级操作，它对性能最大的影响是阻塞等待唤醒机制，挂起线程和恢复线程的操作都需要转入内核态中完成，这些操作给系统的并发性带来了很大的压力。</p><p>到了jdk1.6的时代，对synchronized做了很多的优化，比如自旋锁、自适应自旋锁、偏向锁、轻量级锁等等，这些优化使得<code>synchronized</code>的性能和lock持平甚至超越，未来版本也会随着jdk版本的升级对它进行优化，所以如果在使用<code>synchronized</code>能实现需求的情况下，优先考虑使用它。</p><p>当然，<strong>性能已经不是选择标准</strong></p><h4 id="自旋锁"><a href="#自旋锁" class="headerlink" title="自旋锁"></a>自旋锁</h4><p>大多数时候，锁的锁定状态持续时间是比较短的，而挂起线程和恢复线程的代价又太高了。自旋锁访问加锁资源时，会一直循环的查看是否释放锁。但是会占用CPU。所以自旋锁适用于多核的CPU。但是还有一个问题是当自旋锁递归调用的时候会造成死锁现象。所以慎重使用自旋锁。</p><p>用代码的方式表达，大概是下面这样的：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">InstanceRunnable</span> <span class="keyword">implements</span> <span class="title class_">Runnable</span> &#123;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">int</span> <span class="variable">sharedNum</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">volatile</span> <span class="type">boolean</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">increase</span><span class="params">()</span>&#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">10000</span>; i++) &#123;</span><br><span class="line">                sharedNum++;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">while</span> (lock)&#123;</span><br><span class="line">            &#125;</span><br><span class="line">            lock = <span class="literal">true</span>;</span><br><span class="line">            increase();</span><br><span class="line">            System.out.println(<span class="string">&quot;结果：&quot;</span>+sharedNum);</span><br><span class="line">            lock = <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">InstanceRunnable</span> <span class="variable">runnable</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">InstanceRunnable</span>();</span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(runnable).start();</span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(runnable).start();</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><h4 id="自适应自旋锁"><a href="#自适应自旋锁" class="headerlink" title="自适应自旋锁"></a>自适应自旋锁</h4><p>自适应自旋锁的<strong>自适应</strong>反映在自旋的时间不再固定了。如果在同一个锁对象上，自旋线程之前刚刚获得过锁，且现在持有锁的线程正在运行中，那么虚拟机会认为这次自旋也很有可能会成功，进而允许该线程等待持续相对更长的时间，比如100个循环。反之，如果某个锁自旋很少获得过成功，那么之后再获取锁的时候将可能省略掉自旋过程，以避免浪费处理器资源。</p><h4 id="轻量级锁"><a href="#轻量级锁" class="headerlink" title="轻量级锁"></a>轻量级锁</h4><p>如果锁竞争激烈，我们不得不依赖于重量级锁，让竞争失败的线程阻塞；如果完全没有实际的锁竞争，那么申请重量级锁都是浪费的。对于绝大多数锁，在整个同步周期内都是不存在竞争的。轻量级锁使用CAS操作来获取锁和释放锁，轻量级锁的目标是，减少无实际竞争情况下，使用重量级锁产生的性能消耗，包括系统调用引起的内核态与用户态切换、线程阻塞造成的线程切换等。</p><h4 id="偏向锁"><a href="#偏向锁" class="headerlink" title="偏向锁"></a>偏向锁</h4><p>在没有实际竞争的情况下，还能够针对部分场景继续优化。如果不仅仅没有实际竞争，自始至终，使用锁的线程都只有一个，那么，维护轻量级锁都是浪费的。偏向锁的目标是，减少无竞争且只有一个线程使用锁的情况下，使用轻量级锁产生的性能消耗。轻量级锁每次申请、释放锁都至少需要一次CAS，但偏向锁只有初始化时需要一次CAS。如果有其他线程竞争，它会膨胀为轻量级锁。</p><h4 id="重量级锁"><a href="#重量级锁" class="headerlink" title="重量级锁"></a>重量级锁</h4><p>内置锁在Java中被抽象为监视器锁（monitor）。在JDK 1.6之前，监视器锁可以认为直接对应底层操作系统中的互斥量（mutex）。这种同步方式的成本非常高，包括系统调用引起的内核态与用户态切换、线程阻塞造成的线程切换等。因此，后来称这种锁为“重量级锁”。</p><h4 id="锁消除"><a href="#锁消除" class="headerlink" title="锁消除"></a>锁消除</h4><p>锁消除是一种更为彻底的优化，在 JIT 编译时，对运行上下文进行扫描，去除不可能存在共享资源竞争的锁。消除的判断依据来源于<strong>逃逸分析</strong>的数据支持，也就是说，堆上所有的数据都不会逃逸出去从而被其他线程访问到，这些数据都是在栈上分配内存的，所以是线程私有的，对于这种代码，编译器会自动优化消除锁。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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> String <span class="title function_">connectString</span><span class="params">(String a,String b,String c)</span>&#123;</span><br><span class="line">        <span class="type">StringBuffer</span> <span class="variable">buffer</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StringBuffer</span>();</span><br><span class="line">        buffer.append(a);</span><br><span class="line">        buffer.append(b);</span><br><span class="line">        buffer.append(c);</span><br><span class="line">        <span class="keyword">return</span> buffer.toString();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上面的代码用到了<code>StringBuffer</code>，但是实际上并不存在锁的竞争，所以可以被安全的消除掉。</p><h4 id="锁粗化"><a href="#锁粗化" class="headerlink" title="锁粗化"></a>锁粗化</h4><p>如果存在一连串的操作都对同一个对象进行反复的加锁和解锁，甚至加锁的操作出现在循环体中，那么即使没有线程竞争共享资源，频繁的进行加锁操作也会导致性能损耗。锁粗化的思想就是扩大加锁范围，避免反复的加锁和解锁。比如上面的append操作都是对同一个对象加锁，会把加锁同步的范围扩展到整个操作序列的外部。</p><h3 id="锁相关的一些概念"><a href="#锁相关的一些概念" class="headerlink" title="锁相关的一些概念"></a>锁相关的一些概念</h3><h4 id="可重入锁"><a href="#可重入锁" class="headerlink" title="可重入锁"></a>可重入锁</h4><p>可重入就是说某个线程已经获得某个锁，可以再次获取锁而不会出现死锁。 例如：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 可重入降低了编程复杂性</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WhatReentrant</span> &#123;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Thread</span>(<span class="keyword">new</span> <span class="title class_">Runnable</span>() &#123;</span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line"><span class="keyword">synchronized</span> (<span class="built_in">this</span>) &#123;</span><br><span class="line">System.out.println(<span class="string">&quot;第1次获取锁，这个锁是：&quot;</span> + <span class="built_in">this</span>);</span><br><span class="line"><span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line"><span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line"><span class="keyword">synchronized</span> (<span class="built_in">this</span>) &#123;</span><br><span class="line">System.out.println(<span class="string">&quot;第&quot;</span> + (++index) + <span class="string">&quot;次获取锁，这个锁是：&quot;</span> + <span class="built_in">this</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> (index == <span class="number">10</span>) &#123;</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line">&#125;).start();</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>java中，synchronized和Reentrantlock都是可重入锁。</p><h4 id="公平锁"><a href="#公平锁" class="headerlink" title="公平锁"></a>公平锁</h4><p>加锁前先查看是否有排队等待的线程，有的话优先处理排在前面的线程，先来先得。可以使用ReentrantLock(true)实现公平锁。</p><h4 id="非公平锁"><a href="#非公平锁" class="headerlink" title="非公平锁"></a>非公平锁</h4><p>线程加锁时直接尝试获取锁，获取不到就自动到队尾等待。只要线程进入了等待队列，队列里面依然是FIFO的原则，这个时候非公平锁跟公平锁是一样的。</p><p>非公平锁减少了线程挂起的几率（线程切换的开销），后来的线程有一定几率逃离被挂起的开销（当前线程刚好释放锁的同时，后来的线程刚好获取锁）</p><p>synchronized和ReentrantLock默认都是非公平锁</p><h4 id="乐观锁"><a href="#乐观锁" class="headerlink" title="乐观锁"></a>乐观锁</h4><p>这其实是一种思想，当线程去拿数据的时候，认为别的线程不会修改数据，就不上锁，但是在更新数据的时候会去判断以下其他线程是否修改了数据。通过版本来判断，如果数据被修改了就拒绝更新，之所以叫乐观锁是因为并没有加锁。 cas（compareAndSwap）就是实现的这种思想。</p><p>这种锁一般用于数据库，当一个数据库的读操作远远大于写的操作次数时，使用乐观锁会加大数据库的吞吐量。一般我们会给数据库表增加一个<code>version</code>字段实现乐观锁，更新的时候判断当前version是否被其他线程修改了，比如下面的sql：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">update goods</span><br><span class="line">set name = xxx,state = xxx,version = version + 1</span><br><span class="line">where id = xxx and version = xxx</span><br></pre></td></tr></table></figure><h4 id="悲观锁"><a href="#悲观锁" class="headerlink" title="悲观锁"></a>悲观锁</h4><p>当线程去哪数据的时候，总以为别的线程会去修改数据，所以它每次拿数据的时候都会上锁，别的线程去拿数据的时候就会阻塞。synchronized和ReentrantLock都是悲观锁。</p><h4 id="死锁"><a href="#死锁" class="headerlink" title="死锁"></a>死锁</h4><p>指多个线程因竞争资源而造成的一种僵局（互相等待），若无外力作用，这些进程都将无法向前推进。</p><p>产生的必要条件：必须同时满足以下四个条件，只要有一条不满足，死锁就不会发生。</p><ol><li>互斥条件：在一段时间内某 资源仅为一个进程所占有。此时若有其他进程请求该资源，则请求进程只能等待。</li><li>不剥夺条件：进程所获得的资源在未使用完毕之前，不能被其他进程强行夺走，即只能 由获得该资源的进程自己来释放（只能是主动释放)。</li><li>请求和保持条件：进程已经保持了至少一个资源，但又提出了新的资源请求，而该资源 已被其他进程占有，此时请求进程被阻塞，但对自己已获得的资源保持不放。</li><li>循环等待条件：存在一种进程资源的循环等待链，链中每一个进程已获得的资源同时被 链中下一个进程所请求。</li></ol><p>避免死锁：只需要避免上述4个条件的任意一条即可。</p><ol><li>加锁顺序，线程按照一定的顺序加锁。当多个线程需要相同的一些锁，但是按照不同的顺序加锁，死锁就很容易发生，如果能确保所有的线程都是按照相同的顺序获得锁，那么死锁就不会发生</li><li>加锁时限，线程在尝试获取锁的时候加上一定的时间限制，超过时间则放弃对该锁的请求，并释放自己占有的锁。</li><li>死锁检测，它是一个更好的死锁预防机制，它主要是针对那些不可能实现按序加锁并且锁超时也不可行的场景。每当一个线程获得了锁，会在线程和锁相关的数据结构中（map、graph等等）将其记下。除此之外，每当有线程请求锁，也需要记录在这个数据结构中。当一个线程请求锁失败时，这个线程可以遍历锁的关系图看看是否有死锁发生。例如，线程A请求锁7，但是锁7这个时候被线程B持有，这时线程A就可以检查一下线程B是否已经请求了线程A当前所持有的锁。如果线程B确实有这样的请求，那么就是发生了死锁。那么当检测出死锁时，这些线程该做些什么呢？一个可行的做法是释放所有锁，回退，并且等待一段随机的时间后重试。</li></ol><p>模拟代码写出一个死锁：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DeadLock</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">ResourceA</span> <span class="keyword">implements</span> <span class="title class_">Runnable</span>&#123;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">synchronized</span> (ResourceA.class)&#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;线程: &quot;</span>+ Thread.currentThread().getName() + <span class="string">&quot; 获取资源A&quot;</span>);</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="comment">//为了让第二个线程先获取B的锁，这里休眠一下</span></span><br><span class="line">                    Thread.sleep(<span class="number">1000L</span>);</span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">synchronized</span> (ResourceB.class)&#123;</span><br><span class="line">                    System.out.println(<span class="string">&quot;线程: &quot;</span>+ Thread.currentThread().getName() + <span class="string">&quot; 获取资源B&quot;</span>);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">ResourceB</span> <span class="keyword">implements</span> <span class="title class_">Runnable</span>&#123;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">synchronized</span> (ResourceB.class)&#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;线程: &quot;</span>+ Thread.currentThread().getName() + <span class="string">&quot; 获取资源B&quot;</span>);</span><br><span class="line">                <span class="keyword">synchronized</span> (ResourceA.class)&#123;</span><br><span class="line">                    System.out.println(<span class="string">&quot;线程: &quot;</span>+ Thread.currentThread().getName() + <span class="string">&quot; 获取资源A&quot;</span>);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(<span class="keyword">new</span> <span class="title class_">ResourceA</span>()).start();</span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(<span class="keyword">new</span> <span class="title class_">ResourceB</span>()).start();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里线程1获取资源A的锁，对A锁保持不放的同时获取资源B的锁，线程2获取到资源B的锁，对B锁保持不放的同时获取资源A的锁，两边一直僵持下去。</p><h3 id="其他锁"><a href="#其他锁" class="headerlink" title="其他锁"></a>其他锁</h3><h4 id="数据库锁"><a href="#数据库锁" class="headerlink" title="数据库锁"></a>数据库锁</h4><p>数据库锁简单来说，就是数据库为了保证数据的唯一性，而使各种共享资源在被并发访问变得有序所设计的一种规则。MySql各存储引擎使用了三种类型的锁定机制。</p><ol><li><p>表级锁定</p><p>表级锁定是MySql中最大粒度的锁定机制，该锁定机制最大的特点是实现逻辑非常简单，带来的系统负面影响最小。所以获取锁和释放锁的速度很快。由于表级锁一次会将整个表锁定，所以可以很好的避免困扰我们的死锁问题。<br>当然，锁定颗粒度大所带来最大的负面影响就是出现锁定资源争用的概率也会最高，致使并大度大打折扣。</p></li><li><p>行级锁定</p><p>行级锁定最大的特点就是锁定对象的颗粒度很小，也是目前各大数据库管理软件所实现的锁定颗粒度最小的。由于锁定颗粒度很小，所以发生锁定资源争用的概率也最小，能够给予应用程序尽可能大的并发处理能力而提高一些需要高并发应用系统的整体性能。<br>虽然能够在并发处理能力上面有较大的优势，但是行级锁定也因此带来了不少弊端。由于锁定资源的颗粒度很小，所以每次获取锁和释放锁需要做的事情也更多，带来的消耗自然也就更大了。此外，行级锁定也最容易发生死锁。</p></li><li><p>页级锁定</p><p>页级锁定是MySQL中比较独特的一种锁定级别，在其他数据库管理软件中也并不是太常见。页级锁定的特点是锁定颗粒度介于行级锁定与表级锁之间，所以获取锁定所需要的资源开销，以及所能提供的并发处理能力也同样是介于上面二者之间。另外，页级锁定和行级锁定一样，会发生死锁</p></li></ol><h4 id="分布式锁"><a href="#分布式锁" class="headerlink" title="分布式锁"></a>分布式锁</h4><p>有的时候，我们需要保证一个方法在同一时间内只能被同一个线程执行。在单机环境中，Java中其实提供了很多并发处理相关的API，但是这些API在分布式场景中就无能为力了。也就是说单纯的Java Api并不能提供分布式锁的能力。</p><p>针对分布式锁的实现，目前比较常用的有以下几种方案：</p><ol><li>基于数据库实现分布式锁</li><li>基于缓存实现分布式锁</li><li>基于Zookeeper实现分布式锁</li></ol><p>实现细节请参考这篇文章：<a href="https://www.hollischuang.com/archives/1716">分布式锁的几种实现方式</a></p><p>参考书籍：</p><p><em>深入理解java虚拟机 jvm高级特效与最佳实践 第二版</em></p><p>参考文章：</p><p><a href="https://www.jianshu.com/p/36eedeb3f912">浅谈偏向锁、轻量级锁、重量级锁</a></p><p><a href="https://www.cnblogs.com/linghu-java/p/8944784.html">Java锁—偏向锁、轻量级锁、自旋锁、重量级锁</a></p><p><a href="https://www.jianshu.com/p/f584799f1c77">一张图读懂非公平锁与公平锁</a></p><p><a href="https://www.cnblogs.com/sessionbest/articles/8689071.html">mysql数据库的锁有多少种，怎么编写加锁的sql语句</a></p><p><a href="https://blog.csdn.net/javazejian/article/details/72828483">深入理解Java并发之synchronized实现原理</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h3 id=&quot;什么是锁&quot;&gt;&lt;a href=&quot;#什么是锁&quot; class=&quot;headerlink&quot; title=&quot;什么是锁&quot;&gt;&lt;/a&gt;什么是锁&lt;/h3&gt;&lt;p&gt;锁的概念源于生活，每家每户都有一把锁，只有持有钥匙才能打开锁进入房间。对于程序来说，就是防止其他线程进入，先到的线程进入房间</summary>
      
    
    
    
    <category term="java" scheme="http://example.com/categories/java/"/>
    
    
    <category term="juc" scheme="http://example.com/tags/juc/"/>
    
  </entry>
  
  <entry>
    <title>Android面试</title>
    <link href="http://example.com/2021/03/20/Android%E9%9D%A2%E8%AF%95/"/>
    <id>http://example.com/2021/03/20/Android%E9%9D%A2%E8%AF%95/</id>
    <published>2021-03-20T02:17:17.000Z</published>
    <updated>2025-09-15T02:18:09.317Z</updated>
    
    <content type="html"><![CDATA[<p>首先复习基础，这儿推荐github上的一个项目：</p><p><a href="https://github.com/francistao/LearningNotes">LearningNote</a></p><p>然后就是一些进阶需要掌握的知识</p><h2 id="一-Android相关"><a href="#一-Android相关" class="headerlink" title="一. Android相关"></a>一. Android相关</h2><h3 id="1-mvc-mvp-mvvm三种架构模式"><a href="#1-mvc-mvp-mvvm三种架构模式" class="headerlink" title="1. mvc mvp mvvm三种架构模式"></a>1. mvc mvp mvvm三种架构模式</h3><ul><li>mvc：业务逻辑、数据、界面分离的一种模式，简单的来说，就是通过controller来操作model层的数据，并且返回给view显示。<ul><li>activity不是标准的controller，随着界面逻辑交互的复杂度提升，activity类的职责不断增加，变得臃肿。</li><li>view和model相互耦合，不利于开发。</li></ul></li><li>mvp：主要是提出了presenter层，作为view和model之间沟通的桥梁。<ul><li>程序逻辑放在presenter中处理，完全将view和model进行了分离，不允许他们之间沟通。</li></ul></li><li>mvvm：主要是将presenter改为了viewmodel，和mvp类似，不同的是viewmodel跟view和model进行双向绑定。<ul><li>使用了data binding</li></ul></li></ul><h3 id="2-Android系统结构层次"><a href="#2-Android系统结构层次" class="headerlink" title="2. Android系统结构层次"></a>2. Android系统结构层次</h3><p>Android系统架构分为5层，从下到上依次为 Linux内核层，硬件抽象层，系统运行库层（Native），应用框架层，应用层。</p><ul><li><p>Linux内核层：Android的核心基于Linux内核，在此基础上添加了Android的专用驱动（比如Binder）、系统的安全性、内存管理、进程管理等等。</p></li><li><p>硬件抽象层（HAL）：有了核心还不行，你得需要运行到相应的硬件上才能实现自己的价值吧。而硬件抽象层就是硬件和Linux内核之间的接口，目的就是将硬件抽象化，保护硬件厂商的知识产权（Linux是有开源协议的）</p></li><li><p>系统运行库层：怎么操纵硬件，显示图像到屏幕？这一层就是干这个的，它分为两部分，分别是C++程序库和Android运行时。</p><ol><li><p>C++程序库：被Android系统中的不同组件使用，可以通过应用框架层被开发者使用，下面是主要的程序库：</p><p>| openGL ES       | 3D绘图函数库     |<br>  | :————– | —————- |<br>| Media Framework | 多媒体库         |<br>| SQLite          | 关系型数据库引擎 |<br>| SSL             | 安全套接层       |</p></li></ol></li></ul><ol start="2"><li><p>Android Runtime：ART虚拟机（5.0之后，Dalvik虚拟机被ART取代），ART在应用第一次安装的时候，就会将字节码预编译成机器码存储到本地，这样应用每次运行就无须执行编译了（Dalvik是每次打开都要即时编译），典型的以空间换时间</p></li><li><p>应用框架层：Framework层，这层代码是用java编写的，为开发人员提供了API。</p></li><li><p>应用层。</p></li></ol><h3 id="3-Activity活动的启动模式及应用场景"><a href="#3-Activity活动的启动模式及应用场景" class="headerlink" title="3. Activity活动的启动模式及应用场景"></a>3. Activity活动的启动模式及应用场景</h3><ol><li>standard: 默认的模式，新建一个Activity就在栈中新建一个activity实例。</li><li>singleTop：栈顶复用模式，与standard相比栈顶复用可以有效减少activity重复创建对资源的消耗 。 登录页面 ，wxpay等支付页面</li><li>singleTask：栈内单例模式，栈内只有一个activity实例，栈内已存activity实例，在其他activity中start这个activity，Android直接把这个实例上面其他activity实例踢出栈GC掉。主页面 ，WebView页面、扫一扫页面 ，付款界面</li><li>singleInstance：开辟一个新的栈存放activity。系统Launcher、锁屏键、来电显示等系统应用 。</li></ol><h3 id="4-Android进程间通信的方式"><a href="#4-Android进程间通信的方式" class="headerlink" title="4. Android进程间通信的方式"></a>4. Android进程间通信的方式</h3><pre><code>  1. Broadcast广播，当某个程序向系统发送广播时，其他的应用程序只能被动地接收广播数据  2. Content Provider，多个应用程序之间数据共享的方式（跨进程共享数据） ，应用程序可以完成对数据的增删改查。Android系统本身也提供了很多的Content Provider，比如音频，视频，联系人等信息。  3. 通过AIDL文件，其中AIDL也是通过binder实现进程间通信的。  4. socket</code></pre><h5 id="1-传统的IPC通信方式"><a href="#1-传统的IPC通信方式" class="headerlink" title="1.)传统的IPC通信方式"></a>1.)传统的IPC通信方式</h5><p>Android系统是基于Linux内核的，Linux提供了管道、消息队列、共享内存和socket等IPC机制。那为什么Android还要提供Binder来实现IPC呢？主要是基于性能、稳定性和安全性方面的考虑。</p><p>性能：socket作为通用接口，传输效率低，开销大，主要用到跨网络进程通信。消息队列、共享内存和管道采用存储-转发模式，数据拷贝至少需要两次，共享内存虽然无需拷贝，但是控制复杂，难以使用。而binder只需要拷贝一次，性能上只次于共享内存。</p><p>稳定性：Binder 基于 C&#x2F;S 架构，客户端（Client）有什么需求就丢给服务端（Server）去完成，架构清晰、职责明确又相互独立，自然稳定性更好。</p><p>安全性：Android 为每个安装好的 APP 分配了自己的 UID，故而进程的 UID 是鉴别进程身份的重要标志。传统的 IPC 只能由用户在数据包中填入 UID&#x2F;PID，但这样不可靠，容易被恶意程序利用。可靠的身份标识只有由 IPC 机制在内核中添加。其次传统的 IPC 访问接入点是开放的，只要知道这些接入点的程序都可以和对端建立连接，不管怎样都无法阻止恶意程序通过猜测接收方地址获得连接。</p><h5 id="2-传统IPC通信原理"><a href="#2-传统IPC通信原理" class="headerlink" title="2.)传统IPC通信原理"></a>2.)传统IPC通信原理</h5><p>通常的做法是消息发送方将要发送的数据存放在内存缓存区中，通过系统调用进入内核态。然后内核程序在内核空间分配内存，开辟一块内核缓存区，调用 copy_from_user() 函数将数据从用户空间的内存缓存区拷贝到内核空间的内核缓存区中。同样的，接收方进程在接收数据时在自己的用户空间开辟一块内存缓存区，然后内核程序调用 copy_to_user() 函数将数据从内核缓存区拷贝到接收进程的内存缓存区。这样数据发送方进程和数据接收方进程就完成了一次数据传输，我们称完成了一次进程间通信。</p><p>一次数据传递需要经历：内存缓存区 –&gt; 内核缓存区 –&gt; 内存缓存区，需要 2 次数据拷贝</p><p>接收数据的缓存区由数据接收进程提供，但是接收进程并不知道需要多大的空间来存放将要传递过来的数据，因此只能开辟尽可能大的内存空间或者先调用 API 接收消息头来获取消息体的大小，这两种做法不是浪费空间就是浪费时间。</p><h5 id="3-Binder-IPC实现"><a href="#3-Binder-IPC实现" class="headerlink" title="3.)Binder IPC实现"></a>3.)Binder IPC实现</h5><p>Binder IPC 机制中涉及到的内存映射通过 mmap() 来实现，mmap() 是操作系统中一种<strong>内存映射</strong>的方法。内存映射简单的讲就是将用户空间的一块内存区域映射到内核空间。映射关系建立后，用户对这块内存区域的修改可以直接反应到内核空间；反之内核空间对这段区域的修改也能直接反应到用户空间。</p><p>参考资料：<a href="https://zhuanlan.zhihu.com/p/35519585">写给 Android 应用工程师的 Binder 原理剖析</a></p><h3 id="5-ContentProvider的设计模式"><a href="#5-ContentProvider的设计模式" class="headerlink" title="5. ContentProvider的设计模式"></a>5. ContentProvider的设计模式</h3><p>参考：<a href="https://www.cnblogs.com/Jax/p/6910699.html">写给Android App开发人员的Android底层知识</a></p><h3 id="6-多线程的实现方法（synchronized和lock的异同）"><a href="#6-多线程的实现方法（synchronized和lock的异同）" class="headerlink" title="6. 多线程的实现方法（synchronized和lock的异同）"></a>6. 多线程的实现方法（synchronized和lock的异同）</h3><ol><li>继承Thread类创建线程</li><li>实现Runnable接口创建线程，推荐使用这种方式，可以复用runnable</li><li>实现Callbale接口，通过FutureTask包装器来创建一个带返回值的线程</li></ol><h4 id="synchronized"><a href="#synchronized" class="headerlink" title="synchronized"></a>synchronized</h4><p>在用法上，它是java的关键字，一般我们不太需要关注他的锁的释放，代码执行完毕或者报错会自动释放锁，并且无法判断锁的状态。</p><h4 id="lock"><a href="#lock" class="headerlink" title="lock"></a>lock</h4><p>是一个接口，我们使用<em>ReentrantLock</em> 比较多，有多个获取锁的方式，可以trylock直接返回获取成功或者失败，线程不用一直等待。在finally中必须要释放该锁。</p><h3 id="7-说一下View的事件分发机制"><a href="#7-说一下View的事件分发机制" class="headerlink" title="7. 说一下View的事件分发机制"></a>7. 说一下View的事件分发机制</h3><h4 id="为什么要有事件分发"><a href="#为什么要有事件分发" class="headerlink" title="为什么要有事件分发"></a>为什么要有事件分发</h4><p><em><strong>注：引用G神的博客：<a href="https://www.gcssloop.com/customview/dispatch-touchevent-theory">点击链接直达</a></strong></em></p><p>Android中的view是树形结构的，view可能会重叠在一起，当我们点击的地方有多个view的时候，这个时间该给谁，这就是为什么要有事件分发。</p><p>先来看看view的树形结构：</p><p><img src="http://gcsblog.oss-cn-shanghai.aliyuncs.com/blog/2019-04-29-071556.jpg?gcssloop"></p><p>上面多出来两个东西是<code>phonewindow</code>和<code>decorview</code>，其中，主题颜色和标题栏内容等主要就是decorview来负责显示的，那<code>PhoneWindow</code>是做什么的呢？</p><p><code>PhoneWindow</code>  继承<code>window</code>，并且是<code>window</code>唯一的实现类，<code>window</code>是一个抽象类，是所有视图的最顶层容器，视图的外观和行为都归他管，不论是背景显示，标题栏还是事件处理都是他管理的范畴，它其实就像是View界的太上皇。</p><pre><code>`DecorView` 是 `PhoneWindow` 的一个内部类，其职位相当于小太监，就是跟在 `PhoneWindow` 身边专业为 `PhoneWindow` 服务的，除了自己要干活之外，也负责消息的传递，`PhoneWindow` 的指示通过 `DecorView` 传递给下面的 View，而下面 View 的信息也通过 `DecorView` 回传给 `PhoneWindow`</code></pre><h4 id="事件分发、拦截、消费"><a href="#事件分发、拦截、消费" class="headerlink" title="事件分发、拦截、消费"></a>事件分发、拦截、消费</h4><table><thead><tr><th align="center">类型</th><th align="center">相关方法</th><th align="center">Activity</th><th align="center">ViewGroup</th><th align="center">View</th></tr></thead><tbody><tr><td align="center">事件分发</td><td align="center">dispatchTouchEvent</td><td align="center">√</td><td align="center">√</td><td align="center">√</td></tr><tr><td align="center">事件拦截</td><td align="center">onInterceptTouchEvent</td><td align="center">X</td><td align="center">√</td><td align="center">X</td></tr><tr><td align="center">事件消费</td><td align="center">onTouchEvent</td><td align="center">√</td><td align="center">√</td><td align="center">√</td></tr></tbody></table><p>Activity作为原始的事件分发者，不需要拦截事件，如果需要这个事件不分发下去就行了。</p><p>同样的，view在事件传递的最末端，也不需要拦截事件，不处理回传回去就行了。</p><p>事件在收集之后最先传递给Activity，然后依次向下传递：</p>   <figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Activity -&gt; PhoneWindow -&gt; DectorView -&gt; ViewGroup -&gt; ... -&gt; view</span><br></pre></td></tr></table></figure><p>如果没有任何View消费掉事件，那么这个事件会按照反方向回传，最终传回给Activity，如果最后 Activity 也没有处理，本次事件才会被抛弃 :</p>   <figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Activity &lt;－ PhoneWindow &lt;－ DecorView &lt;－ ViewGroup &lt;－ ... &lt;－ View</span><br></pre></td></tr></table></figure><p>上面的模式是一个非常经典的责任链模式</p><h3 id="8-说一下View从app启动到显示在界面上的绘制流程"><a href="#8-说一下View从app启动到显示在界面上的绘制流程" class="headerlink" title="8. 说一下View从app启动到显示在界面上的绘制流程"></a>8. 说一下View从app启动到显示在界面上的绘制流程</h3><p><em><strong>注：<a href="https://www.jianshu.com/p/c2b38bada5ba">参考链接</a></strong></em></p><p>在activity的attach方法里面，会创建一个PhoneWindow。</p><p>在onCreate中调用setContentView，<code>setContentView</code>是<code>window</code>的一个抽象方法，真正实现类是<code>PhoneWindow</code>：</p>   <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"> <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setContentView</span><span class="params">(<span class="type">int</span> layoutResID)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (mContentParent == <span class="literal">null</span>) &#123;</span><br><span class="line"></span><br><span class="line"><span class="comment">//1.初始化</span></span><br><span class="line">        <span class="comment">//创建DecorView对象和mContentParent对象 ,并将mContentParent关联到DecorView上</span></span><br><span class="line">            installDecor();</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) &#123;</span><br><span class="line">            mContentParent.removeAllViews();<span class="comment">//Activity转场动画相关</span></span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//2.填充Layout</span></span><br><span class="line">        <span class="keyword">if</span> (hasFeature(FEATURE_CONTENT_TRANSITIONS)) &#123;</span><br><span class="line">            <span class="keyword">final</span> <span class="type">Scene</span> <span class="variable">newScene</span> <span class="operator">=</span> Scene.getSceneForLayout(mContentParent, layoutResID,</span><br><span class="line">                    getContext());</span><br><span class="line">            transitionTo(newScene);<span class="comment">//Activity转场动画相关</span></span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">//将Activity设置的布局文件，加载到mContentParent中</span></span><br><span class="line">            mLayoutInflater.inflate(layoutResID, mContentParent);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//让DecorView的内容区域延伸到systemUi下方，防止在扩展时被覆盖，达到全屏、沉浸等不同体验效果。</span></span><br><span class="line">        mContentParent.requestApplyInsets();</span><br><span class="line"></span><br><span class="line"><span class="comment">//3. 通知Activity布局改变</span></span><br><span class="line">        <span class="keyword">final</span> <span class="type">Callback</span> <span class="variable">cb</span> <span class="operator">=</span> getCallback();</span><br><span class="line">        <span class="keyword">if</span> (cb != <span class="literal">null</span> &amp;&amp; !isDestroyed()) &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//触发Activity的onContentChanged方法  </span></span><br><span class="line">            cb.onContentChanged();</span><br><span class="line">        &#125;</span><br><span class="line">        mContentParentExplicitlySet = <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>核心方法就两个：installDecor() 和 mLayoutInflater.inflate(layoutResID, mContentParent) ;</p><p>installDecor会创建一个<code>DecorView</code> 对象，该对象将作为整个应用窗口的根视图。然后配置不同窗口修饰属性（style theme等）。</p><p>mLayoutInflater.inflate就是解析xml，深度优先地递归解析xml，一层层添加到root view上，最终返回root view.解析的部分大致包含两点：1.解析出View对象，2.解析View对应的Params，并设置给View。</p><h3 id="9-知道什么会引起ANR吗-怎么避免"><a href="#9-知道什么会引起ANR吗-怎么避免" class="headerlink" title="9. 知道什么会引起ANR吗 怎么避免"></a>9. 知道什么会引起ANR吗 怎么避免</h3><p>有四种情况会造成ANR发生：</p><ol><li>5秒内无法响应屏幕触摸事件或键盘输出</li><li>在执行前台广播的<code>onReceive()</code>函数时10秒没有处理完成，后台为20秒</li><li>前台服务20秒内，后台服务在200秒内没有执行完成</li><li><code>ContentProvider</code>的<code>publish</code>在10s内没进行完</li></ol><p>如何避免：</p><p>尽量避免在主线程中做耗时操作。  多线程&#x3D;&#x3D;&gt;引出如何实现多线程，线程池的使用</p><p>如何分析ANR：</p><ol><li>产生anr之后，会在<code>data/anr/</code>目录下生成一个文件<code>traces.txt</code></li><li><code>Logcat</code>中查看</li><li>Java线程调用分析</li><li>DDMS分析</li></ol><h3 id="10-有做过app的性能优化吗"><a href="#10-有做过app的性能优化吗" class="headerlink" title="10. 有做过app的性能优化吗"></a>10. 有做过app的性能优化吗</h3><ol><li><p>app启动加速：一个app的启动分为三种不同的状态，其中，我们只需要对第一种状态做优化。对于App来说, 我们可以控制的启动时间线无外乎<code>Application的onCreate</code>，首屏Activity的渲染。</p><ol><li>冷启动：App没有启动过或者App进程被kill，系统中不存在该App进程。此时App的启动需要创建App进程，加载相关资源，启动main thread，初始化首屏Activity等。</li><li>热启动：App进程只是处于后台, 系统只是将其从后台带到前台, 展示给用户。屏幕会显示一个空白的窗口(颜色基于主题), 直至activity渲染完毕。</li><li>温启动：介于冷启动和热启动之间, 一般来说在以下两种情况下发生，<ol><li>用户back退出了App, 然后又启动. App进程可能还在运行, 但是activity需要重建</li><li>用户退出App后, 系统可能由于内存原因将App杀死, 进程和activity都需要重启, 但是可以在onCreate中将被动杀死锁保存的状态(saved instance state)恢复</li></ol></li></ol></li><li><p>布局优化 减少不必要的嵌套</p><ol><li>尽量不要嵌套使用RelativeLayout</li><li>尽量不要在嵌套的LinearLayout中都使用weight属性</li><li>ConstraintLayout</li><li>善用TextView的Drawable减少布局层级</li></ol></li><li><p>响应优化</p></li><li><p>内存优化   bitmap的使用，options的jusdecodeBounds属性，设置只解析bitmap的宽高等，然后使用insimplesize对bitmap进行压缩。在android2.3的时代，bitmap的回收需要调用recycler方法，并且置空，但是之后只需要进行置空操作。</p><p>加载一张大图：使用BitmapRegionDecode进行局部解码，</p><ul><li>decodeRegion(Rect rect, BitmapFactory.Options options)<br>指定rect区域获取图像，options参数不支持inPurgeable，其他都支持</li></ul><p>lru算法的实现&#x3D;&gt; LinkedHashMap</p></li><li><p>电池使用优化</p></li><li><p>网络优化</p><p>参考：<a href="https://www.jianshu.com/p/f7006ab64da7">Android App优化, 要怎么做?</a></p></li></ol><h3 id="11-了解过Android最新技术吗-使用过吗"><a href="#11-了解过Android最新技术吗-使用过吗" class="headerlink" title="11. 了解过Android最新技术吗 使用过吗"></a>11. 了解过Android最新技术吗 使用过吗</h3><h4 id="flutter"><a href="#flutter" class="headerlink" title="flutter"></a>flutter</h4><p>一步手机的刷新率为60hz，当一帧图像绘制完毕后准备绘制下一帧时，显示器会发出一个垂直同步信号（如VSync）， 60Hz的屏幕就会一秒内发出 60次这样的信号。而这个信号主要是用于同步CPU、GPU和显示器的。</p><p>一般地来说，计算机系统中</p><p>，CPU、GPU和显示器以一种特定的方式协作：CPU将计算好的显示内容提交给 GPU，GPU渲染后放入帧缓冲区，然后视频控制器按照同步信号从帧缓冲区取帧数据传递给显示器显示。</p><h4 id="kotlin"><a href="#kotlin" class="headerlink" title="kotlin"></a>kotlin</h4><p>面试几家之后发现都没有问，所以略过</p><h3 id="12-框架源码相关"><a href="#12-框架源码相关" class="headerlink" title="12.框架源码相关"></a>12.框架源码相关</h3><p>看源码要带着问题出发，选好一条路走到黑，不然会有种无从下手的感觉。</p><p>retrofit：主要精髓是动态代理，以及各种设计模式。可以将接口包装成一个http请求，以调用java方法的方式请求api，参考：<a href="https://www.jianshu.com/p/c1a3a881a144">retrofit2源码解析</a></p><p>okhttp：看了retrofit还有什么理由不看okhttp？okhttp设计非常巧妙，如果你知道osi网络七层架构，理解起来就很容易了，也是分层管理的思想。墙裂推荐这篇文章：<a href="https://juejin.im/post/5a704ed05188255a8817f4c9">Android开源框架源码鉴赏：okhttp</a>，文章通俗易懂，值得反复去看</p><p>recyclerview：现在做Android开发都用上它了吧。参考：<a href="https://www.jianshu.com/p/c52b947fe064">recyclerview源码解析系列文章</a></p><p>Fresco&#x2F;Glide：主要是，这几个图片加载库的区别。如果是你，怎么设计图片加载库？多级缓存都问烂了，这儿其实还有个知识点，多图加载肯定使用到线程池，会让你讲怎么用，原理是什么，等等。</p><p>Butterknife： 这儿要注意的地方是，有些人认为原理是利用反射，但是其实不是，它用到的是APT技术，也就没有反射效率低的问题</p><h2 id="二-JAVA相关"><a href="#二-JAVA相关" class="headerlink" title="二. JAVA相关"></a>二. JAVA相关</h2><h3 id="1-说一下JAVA的GC以及内存模型"><a href="#1-说一下JAVA的GC以及内存模型" class="headerlink" title="1. 说一下JAVA的GC以及内存模型"></a>1. 说一下JAVA的GC以及内存模型</h3><p>GC：垃圾回收器，自动释放垃圾占用的空间，让创建的对象不需要像c&#x2F;c++那样手动delete、free掉。</p><p>GC是在什么时候，对什么东西，做了什么事情？</p><h4 id="什么时候"><a href="#什么时候" class="headerlink" title="什么时候"></a>什么时候</h4><p>Java堆内存不足时，GC会被调用。当应用线程在运行,并在运行过程中创建新对象，若这时内存空间不足，JVM就会强制地调用GC线程，以便回收内存用于新的分配。若GC一次之后仍不能满足内存分配的要求，JVM会再进行两次GC作进一步的尝试，若仍无法满足要求，则 JVM将报“out of memory”的错误，Java应用将停止。</p><p>minor gc&#x2F;full gc的触发条件、OOM的触发条件，降低GC的调优的策略 （深入理解jvm）</p><p>minor  gc ：当新生代的eden区满了的时候，会触发gc</p><p>full gc： 当老年代空间不足，方法区空间不足，minor gc进入老年代的时候。大对象</p><h4 id="对什么东西"><a href="#对什么东西" class="headerlink" title="对什么东西"></a>对什么东西</h4><p>从gc root搜索不到，而且经过第一次标记、清理后，仍然没有复活的对象</p><h4 id="做了什么事情"><a href="#做了什么事情" class="headerlink" title="做了什么事情"></a>做了什么事情</h4><p>JVM将堆分成了二个大区新生代（Young）和老年代（Old），新生代又被进一步划分为Eden和Survivor区，而Survivor由FromSpace和ToSpace组成。</p><p>Young中的98%的对象都是死朝生夕死，所以将内存分为一块较大的Eden和两块较小的Survivor1、Survivor2，JVM默认分配是8:1:1，每次调用Eden和其中的Survivor1（FromSpace），当发生回收的时候，将Eden和Survivor1（FromSpace）存活的对象复制到Survivor2（ToSpace）</p><p><em>注：分成三块是为了充分利用内存。原来是只有一块内存，在这上面做标记清除算法，但是gc之后会存在大量不连续的空间，所以有人提出将内存一分为二，将第一块内存存活的对象转移到第二块内存，然后将第一块内存gc。</em></p><p>新生代的GC（Minor GC）：新生代通常存活时间较短基于Copying算法进行回收，所谓Copying算法就是扫描出存活的对象，并复制到一块新的完全未使用的空间中，对应于新生代，就是在Eden和FromSpace或ToSpace之间copy。新生代采用空闲指针的方式来控制GC触发，指针保持最后一个分配的对象在新生代区间的位置，当有新的对象要分配内存时，用于检查空间是否足够，不够就触发GC。当连续分配对象时，对象会逐渐从Eden到Survivor，最后到老年代。</p><p>老年代的GC（Major GC&#x2F;Full GC）：老年代与新生代不同，老年代对象存活的时间比较长、比较稳定，因此采用标记(Mark)算法来进行回收，所谓标记就是扫描出存活的对象，然后再进行回收未被标记的对象，回收后对用空出的空间要么进行合并、要么标记出来便于下次进行分配，总之目的就是要减少内存碎片带来的效率损耗。</p><h4 id="jvm内存模型（注意和java内存模型区分，java内存模型和并发编程相关）"><a href="#jvm内存模型（注意和java内存模型区分，java内存模型和并发编程相关）" class="headerlink" title="jvm内存模型（注意和java内存模型区分，java内存模型和并发编程相关）"></a>jvm内存模型（注意和java内存模型区分，java内存模型和并发编程相关）</h4><ol><li>程序计数器：存放每个程序下一步将执行的jvm指令，如果是native方法，则不会存储任何信息</li><li>jvm栈：是线程私有的，每个线程创建的同时都会创建jvm栈</li><li>堆：JVM用来存储对象实例以及数组值的区域，可以认为Java中所有通过new创建的对象的内存都在此分配，Heap中的对象的内存需要等待GC进行回收。<ol><li>堆是JVM中所有线程共享的，因此在其上进行对象内存的分配均需要进行加锁，这也导致了new对象的开销是比较大的</li><li>Sun Hotspot JVM为了提升对象内存分配的效率，对于所创建的线程都会分配一块独立的空间TLAB（Thread Local Allocation Buffer），其大小由JVM根据运行的情况计算而得，在TLAB上分配对象时不需要加锁，因此JVM在给线程的对象分配内存时会尽量的在TLAB上分配，在这种情况下JVM中分配对象内存的性能和C基本是一样高效的，但如果对象过大的话则仍然是直接使用堆空间分配</li><li>TLAB仅作用于新生代的Eden Space，因此在编写Java程序时，通常多个小的对象比大的对象分配起来更加高效。</li></ol></li><li>方法区（持久代）：存放了所加载的类的信息（名称、修饰符等）、类中的静态变量、类中定义为final类型的常量、类中的Field信息、类中的方法信息，当开发人员在程序中通过Class对象中的getName、isInterface等方法来获取信息时，这些数据都来源于方法区域，同时方法区域也是全局共享的，在一定的条件下它也会被GC，当方法区域需要使用的内存超过其允许的大小时，会抛出OutOfMemory的错误信息。<strong>jdk1.8中被移除了，改用metaspace</strong></li><li>本地方法栈：JVM采用本地方法栈来支持native方法的执行，此区域用于存储每个native方法调用的状态</li><li>运行常量池：存放的为类中的固定的常量信息、方法和Field的引用信息等，其空间从方法区域中分配。JVM在加载类时会为每个class分配一个独立的常量池，但是运行时常量池中的字符串常量池是全局共享的。</li></ol><h3 id="2-Java内存模型"><a href="#2-Java内存模型" class="headerlink" title="2. Java内存模型"></a>2. Java内存模型</h3><p><em>参考：<a href="https://blog.csdn.net/suifeng3051/article/details/52611310">全面理解java内存模型</a>，深入理解java虚拟机</em></p><p>Java内存模型定义了多线程之间共享变量的可见性以及如何在需要的时候对共享变量进行同步。</p><h3 id="3-JAVA的类加载器"><a href="#3-JAVA的类加载器" class="headerlink" title="3. JAVA的类加载器"></a>3. JAVA的类加载器</h3><p>类从被加载到虚拟机内存中开始，到卸载出内存中为止，它的整个生命周期如下：</p><ol><li>加载：主要是在内存中生成代表这个类的java.lang.class对象</li><li>验证：确保class文件中的信息符合当前虚拟机的要求，并且不会危害虚拟机本身</li><li>准备：为类变量分配内存并设置类变量初始值的阶段</li><li>解析（不确定顺序）将常量池中的符号引用替换为直接引用的过程</li><li>初始化：在准备阶段是设置初始值，而在这个阶段是根据我们制定的设置值</li><li>使用（不确定顺序）</li><li>卸载</li></ol><p>类加载模型</p><p>双亲委派模型，从java虚拟机的角度讲，只存在两种不同的类加载器，启动类加载器和所有的其他类加载器，启动类加载器使用C++语言实现，是虚拟机的一部分，其他的类加载器由java语言实现，独立于虚拟机外，对于java开发人员来说，类加载器划分得更细：</p><ol><li>启动类加载器（Bootstrap ClassLoader）：加载虚拟机识别的类库</li><li>扩展类加载器：</li><li>应用程序类加载器</li></ol><p>双亲委派的工作过程：如果一个类加载器收到了类加载的请求，首先它不会自己去尝试加载这个类，而是把这个请求委派给父类去完成，最终都会传送到顶层的启动类加载器中，只有当父加载器反馈自己无法完成这个加载请求的时候，子加载器才会尝试自己去加载。</p><p>Android中常用的类加载器有：<code>DexClassLoader</code>和<code>PathClassLoader</code>，这两个类都继承自<code>BaseDexClassLoader</code>，</p><p><code>PathClassLoader</code>用于加载内部的Dex文件，<code>DexClassLoader</code>多传了一个optimizedDirectory参数 ，可以用来加载外部的apk文件（插件化技术的核心）</p><h3 id="4-热更新原理"><a href="#4-热更新原理" class="headerlink" title="4. 热更新原理"></a>4. 热更新原理</h3><p>代码修复主要有两大方案，一种是阿里系的底层替换方案，另一种是腾讯系的类加载方案，这两种方案各有优劣。</p><p>底层替换方案限制比较多，但是时效性最好，加载快，立即见效</p><p>类加载方案时效性差，需要冷启动才能见效，但修复范围广，限制少。</p><h4 id="底层替换方案"><a href="#底层替换方案" class="headerlink" title="底层替换方案"></a>底层替换方案</h4><p>直接在已加载类中替换掉原有方法，即在原来类基础上进行修改，因此无法实现增减原有类方法或字段，这样会破坏原有类的结构。</p><p><a href="https://blog.csdn.net/itermeng/article/details/79350945">热修复原理以及类加载流程</a></p><h4 id="类加载方案"><a href="#类加载方案" class="headerlink" title="类加载方案"></a>类加载方案</h4><p>在app重新启动后让Classloader加载新的类。因为当app运行到一半时，所需发生变更的类已经被加载过，而在Android上无法对一个类进行卸载操作，若不重启，原来的类还存储于虚拟机中，新类无法被加载。因此只有在下次重启时，在业务逻辑运行之前抢先加载补丁中的新类，这样后续访问此类时，才会Resolve为新类，从而达到热修复的目的。</p><h3 id="5-线程池有哪些？使用场景"><a href="#5-线程池有哪些？使用场景" class="headerlink" title="5. 线程池有哪些？使用场景"></a>5. 线程池有哪些？使用场景</h3><p>参考：<a href="https://www.cnblogs.com/rinack/p/9888717.html">深入源码分析Java线程池的实现原理</a></p><p>池化技术</p><p>简单的来讲就是提前保存大量的资源，以备不时之需。在机器资源有限的情况下，使用池化技术可以大大的提高资源的利用率。比较典型的池化技术有线程池、连接池、内存池、对象池等。</p><p>为什么使用线程池</p><ol><li>在任务众多的情况下，系统要为每一个任务创建一个线程，而任务执行完毕后会销毁每一个线程，所以会造成线程频繁地创建与销毁</li><li>多个线程频繁地创建会占用大量的资源，并且在资源竞争的时候就容易出现问题，同时这么多的线程缺乏一个统一的管理，容易造成界面的卡顿。</li><li>多个线程频繁地销毁，会频繁地调用GC机制，这会使性能降低，又非常耗时。</li></ol><p>常见的线程池：</p><ol><li><code>ThreadPoolExecutor</code> ：基本线程池</li><li><code>FixedThreadPool</code> ：可重用固定线程池，执行长期的任务，性能好很多</li><li><code>CachedThreadPool</code> ：按需创建，执行很多短期异步的小程序或者负载较轻的服务器</li><li><code>SingleThreadPool</code>：单个核线的fixed，一个任务一个任务执行的场景</li><li><code>ScheduledThreadPool</code>：定时延时执行，周期性执行任务的场景</li></ol><p>上面的2-5的线程池，都是基于第一个基本线程池实现的，不同的地方在于核心线程数和存放任务的队列类型不同。</p><p>如何使用</p><p>在<code>Executors</code>这个类中封装了很多方法。</p><p>有两种方法提交任务，submit和execute，其中submit就是将runnable包装成FutureTask 而已，最终调用的还是execuet，所以我们看execute的实现过程</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">execute</span><span class="params">(Runnable command)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (command == <span class="literal">null</span>)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">NullPointerException</span>();</span><br><span class="line">        <span class="type">int</span> <span class="variable">c</span> <span class="operator">=</span> ctl.get();</span><br><span class="line">        <span class="keyword">if</span> (workerCountOf(c) &lt; corePoolSize) &#123; <span class="comment">//直接新建核心线程</span></span><br><span class="line">            <span class="keyword">if</span> (addWorker(command, <span class="literal">true</span>))</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            c = ctl.get();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (isRunning(c) &amp;&amp; workQueue.offer(command)) &#123; <span class="comment">//线程池是否在运行，添加到队列里等待</span></span><br><span class="line">            <span class="type">int</span> <span class="variable">recheck</span> <span class="operator">=</span> ctl.get();</span><br><span class="line">            <span class="keyword">if</span> (! isRunning(recheck) &amp;&amp; remove(command)) <span class="comment">//线程池如果没有在运行，移除队列并拒绝任务</span></span><br><span class="line">                reject(command);</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (workerCountOf(recheck) == <span class="number">0</span>)  <span class="comment">//核心线程满时，创建非核心线程执行任务</span></span><br><span class="line">                addWorker(<span class="literal">null</span>, <span class="literal">false</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (!addWorker(command, <span class="literal">false</span>)) <span class="comment">//拒绝</span></span><br><span class="line">            reject(command);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>主要的工作是：</p><ol><li>工作线程数小于核心线程数时，直接新建核心线程执行任务</li><li>大于核心线程数时，将任务添加进等待队列</li><li>核心线程满时，创建非核心线程执行任务</li><li>工作线程数大于最大线程数时，拒绝任务</li></ol><h2 id="三-网络和算法相关"><a href="#三-网络和算法相关" class="headerlink" title="三. 网络和算法相关"></a>三. 网络和算法相关</h2><h3 id="1-说一下你对TCP-IP的理解"><a href="#1-说一下你对TCP-IP的理解" class="headerlink" title="1. 说一下你对TCP&#x2F;IP的理解"></a>1. 说一下你对TCP&#x2F;IP的理解</h3><p><a href="http://www.ruanyifeng.com/blog/2012/05/internet_protocol_suite_part_i.html">阮一峰-互联网协议入门</a></p><h3 id="2-https加密过程"><a href="#2-https加密过程" class="headerlink" title="2. https加密过程"></a>2. https加密过程</h3><p>注：参考 <a href="https://blog.csdn.net/firehive/article/details/81560425">图解HTTPS协议加密解密全过程</a></p><p>在传输层和应用层之间添加了一个SSL层，变成HTTP先和SSL通信，再由SSL和TCP通信</p><p>加密方式有两种：</p><p>对称密钥加密：加密和解密同用一个密钥。加密时就必须将密钥传送给对方。那密钥如何保证安全传输的？这是一个先有鸡还是先有蛋的问题，传输内容怎么加密的密钥也可以这么加密</p><p>非对称加密：使用一对非对称的密钥。一把叫做私有密钥，一把叫做公开密钥。私有密钥不能让其他任何人知道，而公开密钥则可以随意发布，任何人都可以获得。可以想象成一把钥匙和一个锁头，只是全世界只有你一个人有这把钥匙，你可以把锁头给别人，别人可以用这个锁把重要的东西锁起来，然后发给你，因为只有你一个人有这把钥匙，所以只有你才能看到被这把锁锁起来的东西。</p><p>过程：</p><ol><li>客户端发起Https请求，带上支持的加密协议以及版本</li><li>传输证书：服务端配需要采用HTTPS协议的服务器必须要有一套数字证书，可以自己制作，也可以向组织申请。区别就是自己颁发的证书需要客户端验证通过，才可以继续访问，而使用受信任的公司申请的证书则不会弹出提示页面。服务端给客户端传输公钥，这个公钥包含了很多信息，如证书的颁发机构，过期时间等等 。</li><li>客户端解析证书：这部分工作是由客户端的TLS来完成的，首先会验证公钥是否有效，比如颁发机构，过期时间等等。如果证书没有问题，那么就生成一个随机值。然后用证书对该随机值进行加密。</li><li>传送加密信息：用证书加密后的随机值，目的就是让服务端得到这个随机值，以后客户端和服务端的通信就可以通过这个随机值来进行加密解密了。</li><li>服务端解密随机值，并用这个值加密内容：服务端用私钥解密后，得到了客户端传过来的随机值(私钥)，然后把内容通过该值进行对称加密。所谓对称加密就是，将信息和私钥通过某种算法混合在一起，这样除非知道私钥，不然无法获取内容，而正好客户端和服务端都知道这个私钥，所以只要加密算法够彪悍，私钥够复杂，数据就够安全。</li><li>传输加密后的信息</li><li>客户端解密</li></ol><h3 id="3-操作系统中外部设备与CPU之间怎么通信"><a href="#3-操作系统中外部设备与CPU之间怎么通信" class="headerlink" title="3. 操作系统中外部设备与CPU之间怎么通信"></a>3. 操作系统中外部设备与CPU之间怎么通信</h3><p>注：<a href="https://blog.csdn.net/sinolover/article/details/93592406">一步步编写操作系统</a></p><p>任何不兼容的问题，都可以通过增加一“层”来解决。在cpu和外设之间的这一层就是IO接口。IO接口形式不限，它可以是个电路板，也可以是块芯片，甚至可以是个插槽，它的作用就是在cpu和外设之间相互做协调转换，如cpu和外设速度不匹配，它就是变速箱，cpu和外设信号不通用，它就是翻译机。</p><h3 id="4-散列函数如何解决冲突"><a href="#4-散列函数如何解决冲突" class="headerlink" title="4. 散列函数如何解决冲突"></a>4. 散列函数如何解决冲突</h3><h5 id="开放定址法"><a href="#开放定址法" class="headerlink" title="开放定址法"></a>开放定址法</h5><p>一旦发生冲突，就去寻找下一个空的散列地址，只要散列表足够大，空的散列地址总能找到</p><h5 id="再散列函数法"><a href="#再散列函数法" class="headerlink" title="再散列函数法"></a>再散列函数法</h5><p>实现准备多个计算散列值得函数，发生冲突之后，换一种计算散列值的函数。</p><h5 id="链地址法"><a href="#链地址法" class="headerlink" title="链地址法"></a>链地址法</h5><p>在发生冲突的地方插入一条单链表，将冲突的元素都放到这个链表里。</p><p>java的hashmap就是使用的这种方法解决冲突  &gt;&gt;通过数组+链表+（红黑树，jdk1.8之后对hashmap做了优化）的方式，将hashcode相同的元素串成一串。</p><h5 id="公共溢出区法"><a href="#公共溢出区法" class="headerlink" title="公共溢出区法"></a>公共溢出区法</h5><p>发生冲突之后的都存储到公共溢出区</p><h3 id="5-基本排序算法以及实现原理"><a href="#5-基本排序算法以及实现原理" class="headerlink" title="5. 基本排序算法以及实现原理"></a>5. 基本排序算法以及实现原理</h3><p>推荐书籍：大话数据结构</p><p>多去leetcode刷题</p><ol><li>冒泡排序</li><li>插入排序</li><li>选择排序</li><li>归并排序</li><li>快速排序</li><li>希尔排序</li><li>堆排序</li></ol><h3 id="队列的数据结构"><a href="#队列的数据结构" class="headerlink" title="队列的数据结构"></a>队列的数据结构</h3><p>队列通常可以分为两种类型：</p><ol><li>顺序队列，采用顺序存储，当长度确定时使用</li><li>链式队列，采用链式存储，长度不确定时使用（由链表实现）</li></ol>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;首先复习基础，这儿推荐github上的一个项目：&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/francistao/LearningNotes&quot;&gt;LearningNote&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;然后就是一些进阶需要掌握的知识&lt;/p&gt;
&lt;h2 id=</summary>
      
    
    
    
    <category term="面试" scheme="http://example.com/categories/%E9%9D%A2%E8%AF%95/"/>
    
    
  </entry>
  
  <entry>
    <title>朝花夕拾之几大基础排序算法</title>
    <link href="http://example.com/2021/03/15/%E6%9C%9D%E8%8A%B1%E5%A4%95%E6%8B%BE%E4%B9%8B%E5%87%A0%E5%A4%A7%E5%9F%BA%E7%A1%80%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95/"/>
    <id>http://example.com/2021/03/15/%E6%9C%9D%E8%8A%B1%E5%A4%95%E6%8B%BE%E4%B9%8B%E5%87%A0%E5%A4%A7%E5%9F%BA%E7%A1%80%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95/</id>
    <published>2021-03-15T02:16:05.000Z</published>
    <updated>2025-09-15T02:16:52.313Z</updated>
    
    <content type="html"><![CDATA[<p>算法从来不是死记硬背，而是要知道它背后需要解决什么样的问题，为什么这样设计，</p><h3 id="选择排序"><a href="#选择排序" class="headerlink" title="选择排序"></a>选择排序</h3><p>其实就是挨个查找最小元素的索引，然后交换之。先看图：</p><p><img src="http://upload-images.jianshu.io/upload_images/1940317-b69f69ee21073f80.gif?imageMogr2/auto-orient/strip"></p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">template</span>&lt;<span class="keyword">typename</span>  T&gt;</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">selectionSort</span><span class="params">(T arr[], <span class="type">int</span> n)</span> </span>&#123;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; n; i++)</span><br><span class="line">&#123;</span><br><span class="line"><span class="comment">//寻找在[i,n)之间的最小值的索引</span></span><br><span class="line"><span class="type">int</span> minIndex = i; </span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> j = i<span class="number">+1</span>; j &lt; n; j++)</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">if</span> (arr[j] &lt; arr[minIndex])</span><br><span class="line">&#123;</span><br><span class="line">minIndex = j;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">swap</span>(arr[i], arr[minIndex]);</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="插入排序"><a href="#插入排序" class="headerlink" title="插入排序"></a>插入排序</h3><p><img src="http://upload-images.jianshu.io/upload_images/1940317-9455ff13bc8fbdc6.gif?imageMogr2/auto-orient/strip"></p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">template</span>&lt;<span class="keyword">typename</span> T &gt;</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">insertSort</span><span class="params">(T arr[], <span class="type">int</span> n)</span> </span>&#123;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt; n; i++)</span><br><span class="line">&#123;</span><br><span class="line">T e = arr[i];</span><br><span class="line"><span class="type">int</span> j;</span><br><span class="line"><span class="keyword">for</span> (j = i; j &gt; <span class="number">0</span> &amp;&amp; arr[j - <span class="number">1</span>] &gt; e; j--)</span><br><span class="line">&#123;</span><br><span class="line">arr[j] = arr[j - <span class="number">1</span>];</span><br><span class="line">&#125;</span><br><span class="line">arr[j] = e;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="归并排序"><a href="#归并排序" class="headerlink" title="归并排序"></a>归并排序</h3><p><img src="http://upload-images.jianshu.io/upload_images/1940317-d3d400686bc61c30.gif?imageMogr2/auto-orient/strip"></p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//将arr[l...mid]和arr[mid+1...r]进行归并</span></span><br><span class="line"><span class="keyword">template</span>&lt;<span class="keyword">typename</span>  T&gt;</span><br><span class="line"><span class="type">void</span> __merge(T arr[], <span class="type">int</span> l, <span class="type">int</span> mid, <span class="type">int</span> r) &#123;</span><br><span class="line"><span class="type">int</span> size = r - l + <span class="number">1</span>;</span><br><span class="line">T aux[size];</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = l; i &lt;= r; i++)</span><br><span class="line">aux[i - l] = arr[i];</span><br><span class="line"><span class="type">int</span> i = l, j = mid + <span class="number">1</span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> k = l; k &lt;= r; k++)</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">if</span> (i &gt; mid)</span><br><span class="line">&#123;</span><br><span class="line">aux[k] = aux[j - l];</span><br><span class="line">j++;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span> (j &gt; r) &#123;</span><br><span class="line">aux[k] = aux[i - l];</span><br><span class="line">i++;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span> (aux[i - l] &lt; aux[j - l]) &#123;</span><br><span class="line">aux[k] = aux[i - l];</span><br><span class="line">i++;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span> &#123;</span><br><span class="line">aux[k] = aux[j - l];</span><br><span class="line">j++;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//递归使用归并排序 对arr[l....r]的范围进行排序</span></span><br><span class="line"><span class="keyword">template</span>&lt;<span class="keyword">typename</span> T &gt;</span><br><span class="line"><span class="type">void</span> __mergeSort(T arr[], <span class="type">int</span> l, <span class="type">int</span> r) &#123;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">if (l &gt;= r)</span></span><br><span class="line"><span class="comment">&#123;</span></span><br><span class="line"><span class="comment">return;</span></span><br><span class="line"><span class="comment">&#125;</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">if</span> (r - l &lt;= <span class="number">15</span>)</span><br><span class="line">&#123;</span><br><span class="line">InsertSort::<span class="built_in">insertionSort</span>(arr, l, r);</span><br><span class="line"><span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">int</span> mid = (l + r) / <span class="number">2</span>;</span><br><span class="line">__mergeSort(arr, l, mid);</span><br><span class="line">__mergeSort(arr, mid + <span class="number">1</span>, r);</span><br><span class="line"><span class="keyword">if</span> (arr[mid] &gt; arr[mid + <span class="number">1</span>])    <span class="comment">//第一个优化，如果这个递归出来的数组本身就已经是有序的了，就不需要进行归并了</span></span><br><span class="line">__merge(arr, l, mid, r);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">template</span>&lt;<span class="keyword">typename</span> T &gt;</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">mergeSort</span><span class="params">(T arr[], <span class="type">int</span> n)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">__mergeSort(arr, <span class="number">0</span>, n - <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="快速排序"><a href="#快速排序" class="headerlink" title="快速排序"></a>快速排序</h3><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><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">//对arr[l...r]部分进行partition操作</span></span><br><span class="line"><span class="comment">//返回p，使得arr[l...p-1] &lt; arr[p]  arr[p+1...r] &gt; arr[p]</span></span><br><span class="line"><span class="function"><span class="keyword">template</span>&lt;<span class="keyword">typename</span> T&gt;</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">partition</span><span class="params">(T arr[],<span class="type">int</span> l,<span class="type">int</span> r)</span> </span>&#123;</span><br><span class="line">T v = arr[l];</span><br><span class="line"><span class="comment">//arr[l+1...j]&lt;v   arr[j+1...i) &gt; v</span></span><br><span class="line"><span class="type">int</span> j = l;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = l<span class="number">+1</span>; i &lt;= r; i++)</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">if</span> (arr[i] &lt; v) &#123;</span><br><span class="line"><span class="built_in">swap</span>(arr[i], arr[j<span class="number">+1</span>]);</span><br><span class="line">j++;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">swap</span>(arr[l],arr[j]);</span><br><span class="line"><span class="keyword">return</span> j;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//对arr[l...r]进行快速排序</span></span><br><span class="line"><span class="keyword">template</span>&lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="type">void</span> __quickSort(T arr[],<span class="type">int</span> l ,<span class="type">int</span> r) &#123;</span><br><span class="line"><span class="keyword">if</span> (l &gt;= r)</span><br><span class="line"><span class="keyword">return</span>;</span><br><span class="line"><span class="type">int</span> p = <span class="built_in">partition</span>(arr,l,r);</span><br><span class="line">__quickSort(arr, l, p<span class="number">-1</span>);</span><br><span class="line">__quickSort(arr, p + <span class="number">1</span>, r);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">template</span>&lt;<span class="keyword">typename</span> T&gt;</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">quickSort</span><span class="params">(T arr[], <span class="type">int</span> n)</span> </span>&#123;</span><br><span class="line">__quickSort(arr,<span class="number">0</span>,n<span class="number">-1</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;算法从来不是死记硬背，而是要知道它背后需要解决什么样的问题，为什么这样设计，&lt;/p&gt;
&lt;h3 id=&quot;选择排序&quot;&gt;&lt;a href=&quot;#选择排序&quot; class=&quot;headerlink&quot; title=&quot;选择排序&quot;&gt;&lt;/a&gt;选择排序&lt;/h3&gt;&lt;p&gt;其实就是挨个查找最小元素的索引，</summary>
      
    
    
    
    <category term="算法" scheme="http://example.com/categories/%E7%AE%97%E6%B3%95/"/>
    
    <category term="java" scheme="http://example.com/categories/%E7%AE%97%E6%B3%95/java/"/>
    
    
  </entry>
  
  <entry>
    <title>朝花夕拾之OSI七层参考模型和TCP/IP五层模型</title>
    <link href="http://example.com/2021/03/07/%E6%9C%9D%E8%8A%B1%E5%A4%95%E6%8B%BE%E4%B9%8BOSI%E4%B8%83%E5%B1%82%E5%8F%82%E8%80%83%E6%A8%A1%E5%9E%8B%E5%92%8CTCP-IP%E4%BA%94%E5%B1%82%E6%A8%A1%E5%9E%8B/"/>
    <id>http://example.com/2021/03/07/%E6%9C%9D%E8%8A%B1%E5%A4%95%E6%8B%BE%E4%B9%8BOSI%E4%B8%83%E5%B1%82%E5%8F%82%E8%80%83%E6%A8%A1%E5%9E%8B%E5%92%8CTCP-IP%E4%BA%94%E5%B1%82%E6%A8%A1%E5%9E%8B/</id>
    <published>2021-03-07T02:15:03.000Z</published>
    <updated>2025-09-15T02:15:53.575Z</updated>
    
    <content type="html"><![CDATA[<h1 id="简述OSI七层模型和TCP-IP五层模型"><a href="#简述OSI七层模型和TCP-IP五层模型" class="headerlink" title="简述OSI七层模型和TCP&#x2F;IP五层模型"></a>简述OSI七层模型和TCP&#x2F;IP五层模型</h1><p>复习这块知识的时候，总是让我回想起大学的时光，大学没有好好学习网络相关的基础知识，觉得这些知识在当时看来没有什么实际的意义，现在工作之后，又回顾这些知识，有一种豁然开朗的感觉。</p><p>不知道你们接触网络分层模型的时候，有没有这种疑惑：互联网实现为什么需要分层？</p><p>在我读书的时候，老师总是开头就把各个分层的概念给我说了，让我记住就行，考试要考这些。我是个乖宝宝，老师交代的我当然要听啊，所以就死记硬背，考试过后查完成绩，立马就给忘了。</p><h4 id="OSI七层模型："><a href="#OSI七层模型：" class="headerlink" title="OSI七层模型："></a>OSI七层模型：</h4><ol><li>OSI七层网络模型称为开发式系统互联网参考模型，是一个逻辑上的定义和规范；</li><li>把网络从逻辑上分为七层，每一层都有相应的物理设备</li><li>OSI七层网络模型是一种框架式的设计方法，最主要的功能就是帮助不同类型的主机实现数据传输；</li><li>最大的优点就是将服务、接口和协议三个概念明确的区分起来，随之而来的问题就是复杂且不实用；</li><li>经常使用的是TCP&#x2F;IP五层模型。</li></ol><h4 id="TCP-IP五层模型"><a href="#TCP-IP五层模型" class="headerlink" title="TCP&#x2F;IP五层模型"></a>TCP&#x2F;IP五层模型</h4><p>每一层都呼叫它的下一层提供的网络来完成自己的需求。就像建筑物一样，一层一层堆积，开发者只关注上层应用层，而不用关心下层具体的实现。</p><ol><li><p>物理层：负责光电信号传递方式。集线器工作在物理层。以太网协议。通俗的讲，它就是把电脑连接起来的物理手段，可以用光缆、电缆等方式，主要负责传送0和1的电信号。</p></li><li><p>数据链路层：单纯的0和1的电信号是没有任何意义的，我们需要给它们分组，多少个为一组，这一组的数据是什么类型的数据，数据的发送者、接受者是谁。链路层就是解决这个问题的，它是负责设备之间的数据帧的传输和识别。</p><ol><li>以太网协议：不同的公司实现自己的数据分组，显然不太现实，这个时候就需要一个大家共同遵守的电信号分组协议，以太网协议就是这样的协议</li><li>MAC地址：，发送者和接受者是如何标识呢？以太网规定，连入网络的所有设备，都必须具有”网卡”接口。数据包必须是从一块网卡，传送到另一块网卡。网卡的地址，就是数据包的发送地址和接收地址，这叫做MAC地址。 而数据的发送方，必须提前知道接收方的mac地址，它会向本网络内所有计算机发送数据，让每台计算机自己判断，是否为接收方。这种方式叫做广播。</li></ol></li><li><p>网络层：负责地址管理和路由选择。路由器工作在网络层。 单单依靠mac地址发送数据，规定只能在同一个局域网下实现，虽然在不同的局域网下，技术上也可以实现，但是缺点是效率低下，而且每台计算机都会收到所有消息，这样的设计是不合理到的。所以网络层由此而生，它引进一套新的地址，使得我们能够区分不同的计算机是否属于同一个子网络。这套地址就叫做”网络地址”，简称”网址”，即ip地址。</p></li><li><p>传输层：负责两台主机之间的数据传输。 有了MAC地址和IP地址，我们已经可以在互联网上任意两台主机上建立通信。但是一台主机上有不同的应用程序，我们怎么判断收到的数据是需要哪个应用程序来处理的呢。</p><p>也就是说，我们还需要一个参数，表示这个数据包到底供哪个程序（进程）使用。这个参数就叫做<code>端口</code>。<code>传输层</code>的功能，就是<code>建立端口到端口</code>的通信。相比之下，<code>网络层</code>的功能是建立<code>主机到主机</code>的通信。</p></li><li><p>应用层：负责应用程序之间的沟通。网络编程主要针对的就是应用层。不同的应用程序有不同的功能，要处理的数据也就不同，所以必须要事先规定好格式，否则会无法解读这些数据，比如email，http，ftp等。这些协议构成了应用层。</p></li></ol><p>关于OSI七层模型和TCP&#x2F;IP五层模型的区别，可以用下面这张图概括：</p><p><img src="https://blog-1252348761.cos.ap-chengdu.myqcloud.com/http/osi.png"></p><p>TCP&#x2F;IP五层模型其实就是将OSI七层模型的应用层、表示层、会话层统一成了应用层 。看上去是这样的，实际上OSI和TCP&#x2F;IP是两个不同的东西。只是TCP&#x2F;IP参考了OSI的思想而已。</p><p>为什么要使用TCP&#x2F;IP协议而不是OSI呢，其实在最开始的时候，OSI这个国际标准被提出来了，就要软件&#x2F;硬件厂商去实现了，但是厂商发现OSI协议栈追求全能，追求完善，导致它太过复杂，实现起来太难。而这个时候，民间的大神们借鉴了OSI的思想，又提出了另一套通信协议，就是TCP&#x2F;IP协议。它能完成网络通信工作，实现的难度比OSI要小多了。</p><p>嗯。。。虽然使用的是TCP&#x2F;IP协议，但是OSI网络分类思想是很有参考价值的，毕竟OSI的中文翻译叫：<code>开放系统互联参考模型</code> 嘛。我们在学习网络知识的时候，都是参照OSI模型的，但在使用的时候，都是使用TCP&#x2F;IP模型 。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;简述OSI七层模型和TCP-IP五层模型&quot;&gt;&lt;a href=&quot;#简述OSI七层模型和TCP-IP五层模型&quot; class=&quot;headerlink&quot; title=&quot;简述OSI七层模型和TCP&amp;#x2F;IP五层模型&quot;&gt;&lt;/a&gt;简述OSI七层模型和TCP&amp;#x2F;IP</summary>
      
    
    
    
    <category term="java" scheme="http://example.com/categories/java/"/>
    
    
  </entry>
  
  <entry>
    <title>分享三家奇葩的面试经历</title>
    <link href="http://example.com/2021/02/03/%E5%88%86%E4%BA%AB%E4%B8%89%E5%AE%B6%E5%A5%87%E8%91%A9%E7%9A%84%E9%9D%A2%E8%AF%95%E7%BB%8F%E5%8E%86/"/>
    <id>http://example.com/2021/02/03/%E5%88%86%E4%BA%AB%E4%B8%89%E5%AE%B6%E5%A5%87%E8%91%A9%E7%9A%84%E9%9D%A2%E8%AF%95%E7%BB%8F%E5%8E%86/</id>
    <published>2021-02-03T07:21:21.000Z</published>
    <updated>2025-09-15T07:26:55.175Z</updated>
    
    <content type="html"><![CDATA[<p>今天面了两家公司，最后一家是电话面试，两家公司都要求自带简历，到目前为止，要求自带简历的公司，总体给我的感觉就是差。</p><h2 id="芯软科技"><a href="#芯软科技" class="headerlink" title="芯软科技"></a>芯软科技</h2><ol><li><p>项目介绍</p></li><li><p>挑出你在项目里做的一个模块，谈谈这个模块的整个设计</p><p>这个我是真的不知道怎么回答，你说整个项目怎么设计的我还能画个架构图啥的给你瞅瞅，但是你这说到具体的模块设计，我是不知道怎么说</p></li><li><p>项目并发情况</p></li><li><p>听你说对linux比较熟，环境搭建过吗，有没有遇到什么问题</p><p>搭环境遇到了问题不会百度？还是说这个东西有什么技术难题？？现在都是用docker了阿sir</p></li><li><p>nginx你们一般做什么，正向代理一般用来干啥</p></li><li><p>你会前端啊，说说vue用history打包之后，部署到服务器，nginx需要配置什么</p></li><li><p>说说你的优点</p></li><li><p>假如你最近加班很累（你已经很累了，要死了），但是项目比较急，你想请假怎么跟领导沟通。</p><p>。。。。我tm直接无语了</p></li></ol><p>15分钟解决战斗</p><h2 id="世纪网通"><a href="#世纪网通" class="headerlink" title="世纪网通"></a>世纪网通</h2><ol><li><p>java的序列化和反序列化什么含义  生成的serialVersionUID有什么用</p></li><li><p>yml文件里有一些属性，怎么获取这些属性</p></li><li><p>有一个枚举类，有很多value和code，然后yml中的一个属性对应这个code的值，怎么通过code的值获取到这个对应value</p><p>我想说循环来着，但是觉得没这么简单，所以沉默了半天。。。。</p><p>然后他说枚举类里面有一个方法valueOf</p><p>我：。。。。牛皮</p></li><li><p>final干嘛的，如果有一个final Stirng a &#x3D; “123”，后面还可以改变吗</p><p>？？？</p></li><li><p>一般io操作会捕获异常，然后在catch中释放资源，有更简单的办法吗。</p><p>try-with-resource</p></li><li><p>有一个list，给它按照类里的一个属性排序</p></li><li><p>有个string数组，里面有些空的值，我想去除空的，怎么做</p></li><li><p>线上有个接口速度慢，但是本地测试环境和线上环境是一样的，数据量也是一样，接口速度却很快。怎么排查</p><p>不知道</p></li></ol><h2 id="某研究院"><a href="#某研究院" class="headerlink" title="某研究院"></a>某研究院</h2><p>面了这么多家，他家是第一次把我问的说不上话的。。。</p><ol><li><p>java的继承和多态讲讲，继承在代码里面是怎么使用的</p><p>。。。一开口就知道是老江湖了，直接把我干懵了</p></li><li><p>介绍一下泛型</p><p>完犊子了，这下又把我干懵了。就像突然问你，介绍一下中国，你什么感受</p></li><li><p>设计模式，说说简单工厂模式和抽象工厂模式怎么实现的，区别</p><p>你让我写一个我能写出来，你让我口述。。我语言表达能力真不行</p></li><li><p>java8新特性，讲讲用过stream里的哪些东西吧</p><p>filter，sorted，</p></li><li><p>mysql优化</p><p>说了一堆，然后说道sql操作尽量使用预编译语句的时候，他说：不是mybatis已经解决了嘛</p><p>我：？？？这害得结合框架谈呐</p></li><li><p>redis项目里怎么用</p><p>作用是支撑高性能高并发，然后在项目里具体怎么用的说了下。然后他说我跟他扯半天，一句话：不就是做缓存的嘛</p><p>我：对对对，您说的是</p></li></ol><p>以后不去让自带简历的公司了，特别是小公司。大老远的坐地铁过去，时间花了钱也花了，但是他连两张纸的钱都不愿意出，没啥好说的。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;今天面了两家公司，最后一家是电话面试，两家公司都要求自带简历，到目前为止，要求自带简历的公司，总体给我的感觉就是差。&lt;/p&gt;
&lt;h2 id=&quot;芯软科技&quot;&gt;&lt;a href=&quot;#芯软科技&quot; class=&quot;headerlink&quot; title=&quot;芯软科技&quot;&gt;&lt;/a&gt;芯软科技&lt;/h2</summary>
      
    
    
    
    <category term="面试" scheme="http://example.com/categories/%E9%9D%A2%E8%AF%95/"/>
    
    
  </entry>
  
  <entry>
    <title>SpringCloud之深入理解Feign</title>
    <link href="http://example.com/2021/02/01/SpringCloud%E4%B9%8B%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3Feign/"/>
    <id>http://example.com/2021/02/01/SpringCloud%E4%B9%8B%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3Feign/</id>
    <published>2021-02-01T07:20:17.000Z</published>
    <updated>2025-09-15T07:26:55.171Z</updated>
    
    <content type="html"><![CDATA[<p>微服务中，不同的服务之间相互调用主要有两种方式，一种是RPC方式，另外一种是事件驱动方式，也就是发消息方式。</p><p>RPC的方式就是远程函数调用，像RESTFul，gRPC, DUBBO 都是这种方式。它一般是同步的，可以马上得到结果。在实际中，大多数应用都要求立刻得到结果，这时同步方式更有优势，代码也更简单。而feign自然也是基于这种方式来做的。</p><h2 id="什么是Feign"><a href="#什么是Feign" class="headerlink" title="什么是Feign"></a>什么是Feign</h2><p>feign的github上的介绍：</p><blockquote><p>Feign is a Java to HTTP client binder inspired by Retrofit, JAXRS-2.0, and WebSocket. Feign’s first goal was reducing the complexity of binding Denominator uniformly to HTTP APIs regardless of ReSTfulness.</p></blockquote><p>简单的来说，Feign是由Retrofit，JAXRS-2.0和WebSocket启发的一个java的 RESTFUL api请求工具。 Feign的主要目标是将Java Http Clients变得简单。</p><h2 id="简单使用Feign"><a href="#简单使用Feign" class="headerlink" title="简单使用Feign"></a>简单使用Feign</h2><p>首先在启动类上添加注解：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringCloudApplication</span> </span><br><span class="line"><span class="meta">@EnableFeignClients</span> <span class="comment">// 开启feign客户端 </span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Application</span> &#123; </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;                </span><br><span class="line">        SpringApplication.run(Application.class, args); </span><br><span class="line">&#125; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后编写一个接口：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@FeignClient(name = &quot;item-service&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">UserClient</span>&#123;</span><br><span class="line">    <span class="meta">@GetMapping(&quot;/&#123;id&#125;&quot;)</span></span><br><span class="line">    User <span class="title function_">getUserById</span><span class="params">(<span class="meta">@PathVariable(&quot;id&quot;)</span> Long id)</span>; <span class="comment">//接口代码没有贴出来</span></span><br><span class="line">    </span><br><span class="line">    <span class="meta">@GetMapping(&quot;/list&quot;)</span></span><br><span class="line">    List&lt;User&gt; <span class="title function_">getUserByIds</span><span class="params">(<span class="meta">@RequestParam(&quot;ids&quot;)</span> List&lt;Long&gt; ids)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>最后在controller中调用：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Controller</span> </span><br><span class="line"><span class="meta">@RequestMapping(&quot;/user&quot;)</span> </span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserController</span> &#123; </span><br><span class="line">    <span class="meta">@Autowired</span> </span><br><span class="line">    <span class="keyword">private</span> UserClient userClient; </span><br><span class="line">    </span><br><span class="line">    <span class="meta">@GetMapping</span> </span><br><span class="line">    <span class="meta">@ResponseBody</span> </span><br><span class="line">    <span class="keyword">public</span> User <span class="title function_">queryUserById</span><span class="params">(<span class="meta">@RequestParam(&quot;id&quot;)</span> Long id)</span>&#123; </span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> <span class="built_in">this</span>.userClient.queryUserById(id); </span><br><span class="line">        <span class="keyword">return</span> user; </span><br><span class="line">    &#125; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上面就是feign的整个流程了，只需要创建一个接口来描述http请求，然后我们就可以像调用java方法一样完成api的请求，非常的优雅。</p><h2 id="Feign原理"><a href="#Feign原理" class="headerlink" title="Feign原理"></a>Feign原理</h2><p>从上面的Feign的使用来看，Feign就是充当了一个适配器的角色：将一个java接口翻译成一个http请求，然后用<code>HttpURLConnection</code>去发送这个请求。</p><p>首先通过扫包注入FeignClient的bean，这个源码在<code>FeignClientsRegistrar</code>类中：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">registerDefaultConfiguration</span><span class="params">(AnnotationMetadata metadata,</span></span><br><span class="line"><span class="params">BeanDefinitionRegistry registry)</span> &#123;</span><br><span class="line">Map&lt;String, Object&gt; defaultAttrs = metadata</span><br><span class="line">.getAnnotationAttributes(EnableFeignClients.class.getName(), <span class="literal">true</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (defaultAttrs != <span class="literal">null</span> &amp;&amp; defaultAttrs.containsKey(<span class="string">&quot;defaultConfiguration&quot;</span>)) &#123;</span><br><span class="line">String name;</span><br><span class="line"><span class="keyword">if</span> (metadata.hasEnclosingClass()) &#123;</span><br><span class="line">name = <span class="string">&quot;default.&quot;</span> + metadata.getEnclosingClassName();</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span> &#123;</span><br><span class="line">name = <span class="string">&quot;default.&quot;</span> + metadata.getClassName();</span><br><span class="line">&#125;</span><br><span class="line">registerClientConfiguration(registry, name,</span><br><span class="line">defaultAttrs.get(<span class="string">&quot;defaultConfiguration&quot;</span>));</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这段代码先在启动配置上检查是否有<code>@EnableFeignClients</code>注解，如果有，则开启包扫描，扫描被@FeignClient注解的接口，扫描到FeignClient之后，将其注入到ioc容器中。</p><p>然后就是核心实现，通过jdk动态代理，在请求FeignClient的方法的时候，会拦截这个方法。具体代码在ReflectiveFeign类，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SuppressWarnings(&quot;unchecked&quot;)</span></span><br><span class="line">  <span class="meta">@Override</span></span><br><span class="line">  <span class="keyword">public</span> &lt;T&gt; T <span class="title function_">newInstance</span><span class="params">(Target&lt;T&gt; target)</span> &#123;</span><br><span class="line">    Map&lt;String, MethodHandler&gt; nameToHandler = targetToHandlersByName.apply(target);</span><br><span class="line">    Map&lt;Method, MethodHandler&gt; methodToHandler = <span class="keyword">new</span> <span class="title class_">LinkedHashMap</span>&lt;Method, MethodHandler&gt;();</span><br><span class="line">    List&lt;DefaultMethodHandler&gt; defaultMethodHandlers = <span class="keyword">new</span> <span class="title class_">LinkedList</span>&lt;DefaultMethodHandler&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (Method method : target.type().getMethods()) &#123;</span><br><span class="line">      <span class="keyword">if</span> (method.getDeclaringClass() == Object.class) &#123;</span><br><span class="line">        <span class="keyword">continue</span>;</span><br><span class="line">      &#125; <span class="keyword">else</span> <span class="keyword">if</span>(Util.isDefault(method)) &#123;</span><br><span class="line">        <span class="type">DefaultMethodHandler</span> <span class="variable">handler</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DefaultMethodHandler</span>(method);</span><br><span class="line">        defaultMethodHandlers.add(handler);</span><br><span class="line">        methodToHandler.put(method, handler);</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">InvocationHandler</span> <span class="variable">handler</span> <span class="operator">=</span> factory.create(target, methodToHandler);</span><br><span class="line">    <span class="type">T</span> <span class="variable">proxy</span> <span class="operator">=</span> (T) Proxy.newProxyInstance(target.type().getClassLoader(), <span class="keyword">new</span> <span class="title class_">Class</span>&lt;?&gt;[]&#123;target.type()&#125;, handler);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span>(DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) &#123;</span><br><span class="line">      defaultMethodHandler.bindTo(proxy);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> proxy;</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><p>最终是在<code>SynchronousMethodHandler</code>类进行拦截处理，通过参数生成一个request对象：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line">  <span class="keyword">public</span> Object <span class="title function_">invoke</span><span class="params">(Object[] argv)</span> <span class="keyword">throws</span> Throwable &#123;</span><br><span class="line">    <span class="type">RequestTemplate</span> <span class="variable">template</span> <span class="operator">=</span> buildTemplateFromArgs.create(argv);</span><br><span class="line">    <span class="type">Retryer</span> <span class="variable">retryer</span> <span class="operator">=</span> <span class="built_in">this</span>.retryer.clone();</span><br><span class="line">    <span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">      <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> executeAndDecode(template);</span><br><span class="line">      &#125; <span class="keyword">catch</span> (RetryableException e) &#123;</span><br><span class="line">        retryer.continueOrPropagate(e);</span><br><span class="line">        <span class="keyword">if</span> (logLevel != Logger.Level.NONE) &#123;</span><br><span class="line">          logger.logRetry(metadata.configKey(), logLevel);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">continue</span>;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">Object <span class="title function_">executeAndDecode</span><span class="params">(RequestTemplate template)</span> <span class="keyword">throws</span> Throwable &#123;</span><br><span class="line">    <span class="type">Request</span> <span class="variable">request</span> <span class="operator">=</span> targetRequest(template);</span><br><span class="line">    <span class="comment">//...省略代码</span></span><br><span class="line">    Response response;</span><br><span class="line">    <span class="type">long</span> <span class="variable">start</span> <span class="operator">=</span> System.nanoTime();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      response = client.execute(request, options);</span><br><span class="line">      <span class="comment">// ensure the request is set. <span class="doctag">TODO:</span> remove in Feign 10</span></span><br><span class="line">      response.toBuilder().request(request).build();</span><br><span class="line">    &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">      <span class="keyword">if</span> (logLevel != Logger.Level.NONE) &#123;</span><br><span class="line">        logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">throw</span> errorExecuting(request, e);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//...省略代码</span></span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><p>然后将这个request对象交个client处理，处理完成之后获取到response。client是一个接口，默认的实现为它的一个静态内部类<code>Default</code>，在这个类中，使用HttpURLConnection完成的网络请求：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">public</span> Response <span class="title function_">execute</span><span class="params">(Request request, Options options)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">      <span class="type">HttpURLConnection</span> <span class="variable">connection</span> <span class="operator">=</span> convertAndSend(request, options);</span><br><span class="line">      <span class="keyword">return</span> convertResponse(connection).toBuilder().request(request).build();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="Feign负载均衡实现"><a href="#Feign负载均衡实现" class="headerlink" title="Feign负载均衡实现"></a>Feign负载均衡实现</h2><p>最终向容器中注入的是LoadBalancerFeignClient，即负载均衡客户端：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><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">@Override</span></span><br><span class="line"><span class="keyword">public</span> Response <span class="title function_">execute</span><span class="params">(Request request, Request.Options options)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line"><span class="type">URI</span> <span class="variable">asUri</span> <span class="operator">=</span> URI.create(request.url());</span><br><span class="line"><span class="type">String</span> <span class="variable">clientName</span> <span class="operator">=</span> asUri.getHost();</span><br><span class="line"><span class="type">URI</span> <span class="variable">uriWithoutHost</span> <span class="operator">=</span> cleanUrl(request.url(), clientName);</span><br><span class="line">FeignLoadBalancer.<span class="type">RibbonRequest</span> <span class="variable">ribbonRequest</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FeignLoadBalancer</span>.RibbonRequest(</span><br><span class="line"><span class="built_in">this</span>.delegate, request, uriWithoutHost);</span><br><span class="line"></span><br><span class="line"><span class="type">IClientConfig</span> <span class="variable">requestConfig</span> <span class="operator">=</span> getClientConfig(options, clientName);</span><br><span class="line"><span class="keyword">return</span> lbClient(clientName).executeWithLoadBalancer(ribbonRequest,</span><br><span class="line">requestConfig).toResponse();</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">catch</span> (ClientException e) &#123;</span><br><span class="line"><span class="type">IOException</span> <span class="variable">io</span> <span class="operator">=</span> findIOException(e);</span><br><span class="line"><span class="keyword">if</span> (io != <span class="literal">null</span>) &#123;</span><br><span class="line"><span class="keyword">throw</span> io;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到是调用execute方法，最终是通过executeWithLoadBalancer方法获取结果的：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> T <span class="title function_">executeWithLoadBalancer</span><span class="params">(<span class="keyword">final</span> S request, <span class="keyword">final</span> IClientConfig requestConfig)</span> <span class="keyword">throws</span> ClientException &#123;</span><br><span class="line">        LoadBalancerCommand&lt;T&gt; command = buildLoadBalancerCommand(request, requestConfig);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> command.submit(</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">ServerOperation</span>&lt;T&gt;() &#123;</span><br><span class="line">                    <span class="meta">@Override</span></span><br><span class="line">                    <span class="keyword">public</span> Observable&lt;T&gt; <span class="title function_">call</span><span class="params">(Server server)</span> &#123;</span><br><span class="line">                        <span class="type">URI</span> <span class="variable">finalUri</span> <span class="operator">=</span> reconstructURIWithServer(server, request.getUri());</span><br><span class="line">                        <span class="type">S</span> <span class="variable">requestForServer</span> <span class="operator">=</span> (S) request.replaceUri(finalUri);</span><br><span class="line">                        <span class="keyword">try</span> &#123;</span><br><span class="line">                            <span class="keyword">return</span> Observable.just(AbstractLoadBalancerAwareClient.<span class="built_in">this</span>.execute(requestForServer, requestConfig));</span><br><span class="line">                        &#125; </span><br><span class="line">                        <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">                            <span class="keyword">return</span> Observable.error(e);</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;)</span><br><span class="line">                .toBlocking()</span><br><span class="line">                .single();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="type">Throwable</span> <span class="variable">t</span> <span class="operator">=</span> e.getCause();</span><br><span class="line">            <span class="keyword">if</span> (t <span class="keyword">instanceof</span> ClientException) &#123;</span><br><span class="line">                <span class="keyword">throw</span> (ClientException) t;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ClientException</span>(e);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>最终在submit方法上：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Use the load balancer</span></span><br><span class="line">Observable&lt;T&gt; o = </span><br><span class="line">    (server == <span class="literal">null</span> ? selectServer() : Observable.just(server))</span><br><span class="line">     .concatMap(<span class="keyword">new</span> <span class="title class_">Func1</span>&lt;Server, Observable&lt;T&gt;&gt;() &#123;</span><br><span class="line">     <span class="meta">@Override</span></span><br><span class="line">     <span class="comment">// Called for each server being selected</span></span><br><span class="line">      <span class="keyword">public</span> Observable&lt;T&gt; <span class="title function_">call</span><span class="params">(Server server)</span> &#123;</span><br><span class="line">       <span class="comment">//...省略代码</span></span><br><span class="line">&#125;);               </span><br></pre></td></tr></table></figure><p>通过selectServer方法，实现负载均衡：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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">private</span> Observable&lt;Server&gt; <span class="title function_">selectServer</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> Observable.create(<span class="keyword">new</span> <span class="title class_">OnSubscribe</span>&lt;Server&gt;() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">call</span><span class="params">(Subscriber&lt;? <span class="built_in">super</span> Server&gt; next)</span> &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="type">Server</span> <span class="variable">server</span> <span class="operator">=</span> loadBalancerContext.getServerFromLoadBalancer(loadBalancerURI, loadBalancerKey);   </span><br><span class="line">                    next.onNext(server);</span><br><span class="line">                    next.onCompleted();</span><br><span class="line">                &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">                    next.onError(e);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125; </span><br></pre></td></tr></table></figure><p>最终交给<code>loadBalancerContext</code>也就是Ribbon来做的。</p><p>回顾：</p><ol><li>通过@EnableFeignClients开启FeignClient</li><li>扫包，将@FeignClient注解的类加入到ioc容器</li><li>接口方法被调用，通过jdk动态代理拦截，生成Request</li><li>Requset交给Client处理，Client又被包装为LoadBalanceClient类，这个类结合Ribbon做负载均衡，最终生成Response</li></ol>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;微服务中，不同的服务之间相互调用主要有两种方式，一种是RPC方式，另外一种是事件驱动方式，也就是发消息方式。&lt;/p&gt;
&lt;p&gt;RPC的方式就是远程函数调用，像RESTFul，gRPC, DUBBO 都是这种方式。它一般是同步的，可以马上得到结果。在实际中，大多数应用都要求立刻</summary>
      
    
    
    
    <category term="java" scheme="http://example.com/categories/java/"/>
    
    
  </entry>
  
  <entry>
    <title>朝花夕拾之socket的基本使用以及mina框架简单介绍</title>
    <link href="http://example.com/2020/08/31/%E6%9C%9D%E8%8A%B1%E5%A4%95%E6%8B%BE%E4%B9%8Bsocket%E7%9A%84%E5%9F%BA%E6%9C%AC%E4%BD%BF%E7%94%A8%E4%BB%A5%E5%8F%8Amina%E6%A1%86%E6%9E%B6%E7%AE%80%E5%8D%95%E4%BB%8B%E7%BB%8D/"/>
    <id>http://example.com/2020/08/31/%E6%9C%9D%E8%8A%B1%E5%A4%95%E6%8B%BE%E4%B9%8Bsocket%E7%9A%84%E5%9F%BA%E6%9C%AC%E4%BD%BF%E7%94%A8%E4%BB%A5%E5%8F%8Amina%E6%A1%86%E6%9E%B6%E7%AE%80%E5%8D%95%E4%BB%8B%E7%BB%8D/</id>
    <published>2020-08-31T02:13:24.000Z</published>
    <updated>2025-09-15T02:14:39.243Z</updated>
    
    <content type="html"><![CDATA[<p>工欲善其事，必先利其器，从互联网诞生到现在，基本上所有的程序都是网络程序，很少有单机版的程序了。 而网络编程的本质是两个设备之间的数据交换，当然，在计算机网络中，设备主要指计算机。我们现在进行网络编程，基本上都是使用已经封装好的框架，毕竟自己实现维护一套网络编程框架，费时费力不说，做出来之后还不一定好用，有那时间多保养保养头发。</p><p><img src="https://blog-1252348761.cos.ap-chengdu.myqcloud.com/camera/toufa.jpg"></p><h3 id="Socket简介"><a href="#Socket简介" class="headerlink" title="Socket简介"></a>Socket简介</h3><p>记得大学学习java的时候，并没有接触网络编程相关的基础知识，一直都是单机版程序，没有见识到网络编程的美妙，所以对socket这个东西既熟悉又陌生。熟悉的原因是在MFC实验课中接触到socket知识的，没错，就是那门古老的开发语言，现在已经销声匿迹了，陌生的原因大概也就不言而喻了。好了，说了那么多，那socket到底是什么呢？</p><p>Socket是应用层与TCP&#x2F;IP协议族通信的<strong>中间软件抽象层</strong>，它是<strong>一组接口</strong> 。很多同学会把tcp、udp协议和socket搞混，其实Socket只是一种连接模式，不是协议。tcp、udp是两个最基本的协议，很多其它协议都是基于这两个协议。</p><p>用socket可以创建tcp连接，也可以创建udp连接，这意味着，用socket可以创建任何协议的连接。简单的来说，socket相当于一艘船，你把目的地（ip+端口）告诉它，把要运输的货物搬上去，然后它将货物送往目的地。这个货物具体需要怎么运输，运输完成之后是否还要通知你已经到达目的地，这就是实现协议的区别了。</p><h4 id="Socket使用"><a href="#Socket使用" class="headerlink" title="Socket使用"></a>Socket使用</h4><p>首先来回顾一下socket的基本使用，创建一个服务端所需步骤</p><ol><li>创建ServerSocket对象绑定监听端口。</li><li>通过accept()方法监听客户端的请求。</li><li>建立连接后，通过输入输出流读取客户端发送的请求信息。</li><li>通过输出流向客户端发送请求信息。</li><li>关闭相关资源。</li></ol><p>嗯。。。Talk is cheap,Show me the code：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SocketServer</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">//第一步</span></span><br><span class="line">        <span class="type">SocketServer</span> <span class="variable">socketServer</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SocketServer</span>();</span><br><span class="line">        socketServer.startServer(<span class="number">2333</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">startServer</span><span class="params">(<span class="type">int</span> port)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">ServerSocket</span> <span class="variable">serverSocket</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ServerSocket</span>(port);</span><br><span class="line">            System.out.println(<span class="string">&quot;服务器已启动,等待客户连接...&quot;</span>);</span><br><span class="line">            <span class="comment">//第二步 调用accept()方法开始监听，等待客户端的连接 这个方法会阻塞当前线程</span></span><br><span class="line">            <span class="type">Socket</span> <span class="variable">socket</span> <span class="operator">=</span> serverSocket.accept();</span><br><span class="line">            System.out.println(<span class="string">&quot;客户端连接成功&quot;</span>);</span><br><span class="line">            <span class="comment">//第三步 建立输入输出流读数据</span></span><br><span class="line">            <span class="type">BufferedReader</span> <span class="variable">bufferedReader</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BufferedReader</span>(<span class="keyword">new</span> <span class="title class_">InputStreamReader</span>(socket.getInputStream()));</span><br><span class="line">            <span class="type">BufferedWriter</span> <span class="variable">bufferedWriter</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BufferedWriter</span>(<span class="keyword">new</span> <span class="title class_">OutputStreamWriter</span>(socket.getOutputStream()));</span><br><span class="line">            String receivedMsg;</span><br><span class="line">            <span class="keyword">while</span> ((receivedMsg = bufferedReader.readLine()) != <span class="literal">null</span> &amp;&amp; !(<span class="string">&quot;end&quot;</span>).equals(receivedMsg)) &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;客户端：&quot;</span> + receivedMsg);</span><br><span class="line">                <span class="comment">//第四步 给客户端发送请求</span></span><br><span class="line">                <span class="type">String</span> <span class="variable">response</span> <span class="operator">=</span> <span class="string">&quot;hello client&quot;</span>;</span><br><span class="line">                System.out.println(<span class="string">&quot;我(服务端)：&quot;</span> + response);</span><br><span class="line">                bufferedWriter.write(response+ <span class="string">&quot;\n&quot;</span>);</span><br><span class="line">                bufferedWriter.flush();</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">//关闭相关资源</span></span><br><span class="line">            socket.close();</span><br><span class="line">            serverSocket.close();</span><br><span class="line">            bufferedWriter.close();</span><br><span class="line">            bufferedReader.close();</span><br><span class="line"></span><br><span class="line">        &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>创建一个客户端所需步骤，其实和服务端代码差不多：</p><ol><li>创建Socket对象，指明需要连接的服务器的地址和端口。</li><li>建立连接后，通过输出流向服务器发送请求信息。</li><li>通过输入流获取服务器的响应信息。</li><li>关闭相关资源</li></ol><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SocketClient</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span>&#123;</span><br><span class="line">        <span class="type">SocketClient</span> <span class="variable">socketClient</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SocketClient</span>();</span><br><span class="line">        socketClient.startClient(<span class="number">2333</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">startClient</span><span class="params">(<span class="type">int</span> port)</span>&#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">Socket</span> <span class="variable">clientSocket</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Socket</span>(<span class="string">&quot;localhost&quot;</span>,port);</span><br><span class="line">            System.out.println(<span class="string">&quot;客户端已启动&quot;</span>);</span><br><span class="line">            <span class="comment">//给服务器发消息</span></span><br><span class="line">            <span class="type">BufferedWriter</span> <span class="variable">bufferedWriter</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BufferedWriter</span>(<span class="keyword">new</span> <span class="title class_">OutputStreamWriter</span>(clientSocket.getOutputStream()));</span><br><span class="line">            <span class="comment">//接收服务器传过来的消息</span></span><br><span class="line">            <span class="type">BufferedReader</span> <span class="variable">bufferedReader</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BufferedReader</span>(<span class="keyword">new</span> <span class="title class_">InputStreamReader</span>(clientSocket.getInputStream()));</span><br><span class="line">            <span class="comment">//键盘输入消息  发送给服务端</span></span><br><span class="line">            <span class="type">BufferedReader</span> <span class="variable">inputReader</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BufferedReader</span>(<span class="keyword">new</span> <span class="title class_">InputStreamReader</span>(System.in));</span><br><span class="line">            <span class="type">String</span> <span class="variable">readLine</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">            <span class="keyword">while</span> (!(readLine = inputReader.readLine()).equals(<span class="string">&quot;bye&quot;</span>))&#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;我(客户端)：&quot;</span> + readLine);</span><br><span class="line">                <span class="comment">//将键盘输入的消息发送给服务器</span></span><br><span class="line">                bufferedWriter.write(readLine+<span class="string">&quot;\n&quot;</span>);</span><br><span class="line">                bufferedWriter.flush();</span><br><span class="line">                <span class="type">String</span> <span class="variable">response</span> <span class="operator">=</span> bufferedReader.readLine();</span><br><span class="line">                System.out.println(<span class="string">&quot;服务端: &quot;</span> + response);</span><br><span class="line">            &#125;</span><br><span class="line">            bufferedWriter.close();</span><br><span class="line">            inputReader.close();</span><br><span class="line">            clientSocket.close();</span><br><span class="line">        &#125;<span class="keyword">catch</span> (IOException e)&#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>以上对于socket的示例比较简单，只实现了客户端给服务器发送消息，服务器收到消息并回复客户端。现实的情况其实很复杂，如服务器高并发的处理，客户端怎么监听服务器随时发来的消息，客户端断线重连机制，心跳包的处理，还有在对消息的拆包、粘包的处理等等。而引入mina框架，我们不必关注复杂的网络通信的实现，只需专注于具体的业务逻辑。</p><h3 id="Mina框架简介"><a href="#Mina框架简介" class="headerlink" title="Mina框架简介"></a>Mina框架简介</h3><p>MINA框架是对java的NIO包的一个封装，简化了NIO程序开发的难度，封装了很多底层的细节，让开发者把精力集中到业务逻辑上来。可能有一些同学不知道NIO是什么，这里简单介绍一下，NIO就是new IO，是jdk1.4引入的，它是同步非阻塞的，比如一个服务器多个客户端，客户端发送的请求都会注册到服务器的多路复用器上，多路复用器轮询到连接有I&#x2F;O请求时才启动一个线程进行处理。它适用于连接数目多且连接比较短的架构，比如聊天服务器。</p><h4 id="mina简单使用"><a href="#mina简单使用" class="headerlink" title="mina简单使用"></a>mina简单使用</h4><p>Mina 的API 将真正的网络通信与我们的应用程序隔离开来，你只需要关心你要发送、接收的数据以及你的业务逻辑即可。</p><p>先看看我们实现的效果图(由于是视频转gif，可能不太好看出来，就是一个简单的文本通讯)：</p><p><img src="https://blog-1252348761.cos.ap-chengdu.myqcloud.com/camera/server-video2gif_20190325_162033.gif" alt="server"><img src="https://blog-1252348761.cos.ap-chengdu.myqcloud.com/camera/client_20190325225502.gif" alt="client"></p><h5 id="创建服务端"><a href="#创建服务端" class="headerlink" title="创建服务端"></a>创建服务端</h5><p>前面说mina将网络通信与我们的应用程序隔离开来，那我们看看怎么样实现一个TCP的服务端，最近在学习kotlin，以后的代码应该都用kotlin展示了：</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//创建一个非阻塞的service端的socket</span></span><br><span class="line"><span class="keyword">val</span> acceptor = NioSocketAcceptor()</span><br><span class="line"><span class="comment">//设置编解码器  ProtocolCodecFilter拦截器 网络传输需要将对象转换为字节流</span></span><br><span class="line">acceptor.filterChain.addLast(<span class="string">&quot;codec&quot;</span>,ProtocolCodecFilter(TextLineCodecFactory()))</span><br><span class="line"><span class="comment">//设置读取数据的缓冲区大小</span></span><br><span class="line">acceptor.sessionConfig.readBufferSize = <span class="number">2048</span></span><br><span class="line"><span class="comment">//读写通道10秒内无操作进入空闲状态 </span></span><br><span class="line">acceptor.sessionConfig.setIdleTime(IdleStatus.BOTH_IDLE, <span class="number">10</span>)</span><br><span class="line"><span class="comment">//绑定端口</span></span><br><span class="line">acceptor.bind(InetSocketAddress(<span class="number">8080</span>))</span><br></pre></td></tr></table></figure><p>这段代码我们就初始化了一个TCP服务端，其中，编解码器使用的是mina自带的换行符编解码器工厂，设置编解码器是因为在网络上传输数据时，发送端发送数据需要将对象转换为字节流进行传输，接收端收到数据后再将字节流转换回来。相当于双方约定一套规则，具体规则可以自己定，也可以用现成的。我这里只需要发送文本，就用内置的啦。</p><p>网络通信已经实现，那发送、接收数据呢？我们具体的业务逻辑都在IoHandler这个类中进行处理，编写一个类继承IoHandlerAdapter ，并重写它的几个方法，记得在bind端口之前调用<strong>acceptor.setHandler(MyIoHandlerAdapter())</strong>，不然无法监听到具体事件 ：</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><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="comment">/**</span></span><br><span class="line"><span class="comment">     * 向客户端发送消息后会调用此方法</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> session</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> message</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Throws(Exception::class)</span></span><br><span class="line">    <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">messageSent</span><span class="params">(session: <span class="type">IoSession</span>?, message: <span class="type">Any</span>?)</span></span> &#123;</span><br><span class="line">        <span class="keyword">super</span>.messageSent(session, message)</span><br><span class="line">        LogUtils.i(<span class="string">&quot;服务器发送消息成功&quot;</span>)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 从端口接受消息，会响应此方法来对消息进行处理</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> session</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> message</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Throws(Exception::class)</span></span><br><span class="line">    <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">messageReceived</span><span class="params">(session: <span class="type">IoSession</span>?, message: <span class="type">Any</span>?)</span></span> &#123;</span><br><span class="line">        <span class="keyword">super</span>.messageReceived(session, message)</span><br><span class="line">        <span class="keyword">val</span> msg = message!!.toString()</span><br><span class="line">        LogUtils.i(<span class="string">&quot;服务器接收消息成功：<span class="variable">$msg</span>&quot;</span>)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 服务器与客户端创建连接</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> session</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Throws(Exception::class)</span></span><br><span class="line">    <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">sessionCreated</span><span class="params">(session: <span class="type">IoSession</span>?)</span></span> &#123;</span><br><span class="line">        <span class="keyword">super</span>.sessionCreated(session)</span><br><span class="line">        LogUtils.i(<span class="string">&quot;服务器与客户端创建连接&quot;</span>)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 服务器与客户端连接打开</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> session</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Throws(Exception::class)</span></span><br><span class="line">    <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">sessionOpened</span><span class="params">(session: <span class="type">IoSession</span>?)</span></span> &#123;</span><br><span class="line">        <span class="keyword">super</span>.sessionOpened(session)</span><br><span class="line">        LogUtils.i(<span class="string">&quot;服务器与客户端连接打开&quot;</span>)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 关闭与客户端的连接时会调用此方法</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> session</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Throws(Exception::class)</span></span><br><span class="line">    <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">sessionClosed</span><span class="params">(session: <span class="type">IoSession</span>?)</span></span> &#123;</span><br><span class="line">        <span class="keyword">super</span>.sessionClosed(session)</span><br><span class="line">        LogUtils.i(<span class="string">&quot;关闭与客户端的连接时会调用此方法&quot;</span>)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 服务器进入空闲状态</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> session</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> status</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Throws(Exception::class)</span></span><br><span class="line">    <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">sessionIdle</span><span class="params">(session: <span class="type">IoSession</span>?, status: <span class="type">IdleStatus</span>?)</span></span> &#123;</span><br><span class="line">        <span class="keyword">super</span>.sessionIdle(session, status)</span><br><span class="line">        LogUtils.i(<span class="string">&quot;服务器进入空闲状态&quot;</span>)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 异常</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> session</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> cause</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Throws(Exception::class)</span></span><br><span class="line">    <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">exceptionCaught</span><span class="params">(session: <span class="type">IoSession</span>?, cause: <span class="type">Throwable</span>)</span></span> &#123;</span><br><span class="line">        <span class="keyword">super</span>.exceptionCaught(session, cause)</span><br><span class="line">        LogUtils.i(<span class="string">&quot;服务器异常<span class="variable">$cause</span>&quot;</span>)</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>大家应该注意到<code>IoSession</code>这个东西了，每一个方法参数里都有它，那它具体是干什么的呢？</p><p><code>IoSession</code>是一个接口，这个接口用于表示Server 端与Client 端的连接，<code>IoAcceptor.accept()</code>的时候返回实例。这个接口有如下常用的方法：</p><ol><li><code>WriteFuture write(Object message)：</code>这个方法用于写数据，该操作是异步的。</li><li><code>CloseFuture close(boolean immediately)：</code>这个方法用于关闭IoSession，该操作也是异步的，参数指定true 表示立即关闭，否则就在所有的写操作都flush 之后再关闭。</li><li><code>Object setAttribute(Object key,Object value)：</code>这个方法用于给我们向会话中添加一些属性，这样可以在会话过程中都可以使用，类似于HttpSession 的setAttrbute()方法。IoSession 内部使用同步的HashMap 存储你添加的自定义属性。</li><li><code>SocketAddress getRemoteAddress()：</code>这个方法获取远端连接的套接字地址。</li><li><code>void suspendWrite()：</code>这个方法用于挂起写操作，那么有void resumeWrite()方法与之配对。对于read()方法同样适用。</li><li><code>ReadFuture read()：</code>这个方法用于读取数据， 但默认是不能使用的， 你需要调用IoSessionConfig 的setUseReadOperation(true)才可以使用这个异步读取的方法。一般我们不会用到这个方法，因为这个方法的内部实现是将数据保存到一个BlockingQueue，假如是Server 端，因为大量的Client 端发送的数据在Server 端都这么读取，那么可能会导致内存泄漏，但对于Client，可能有的时候会比较便利。</li><li><code>IoService getService()：</code>这个方法返回与当前会话对象关联的IoService 实例。</li></ol><p>换言之，拿到<code>IoSession</code>就能够进行两端打call了，什么？你问我怎么打call？</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> <span class="title">sendText</span><span class="params">(message: <span class="type">String</span>,client IoSession)</span></span>&#123;</span><br><span class="line">    <span class="keyword">var</span> ioBuffer = IoBuffer.allocate(message.toByteArray().size)</span><br><span class="line">    ioBuffer.put(message.toByteArray())</span><br><span class="line">    ioBuffer.flip()</span><br><span class="line">    client.write(ioBuffer)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="创建客户端"><a href="#创建客户端" class="headerlink" title="创建客户端"></a>创建客户端</h5><p>无论是Server 端还是Client 端，在Mina中的执行流程都是一样的。唯一不同的就是IoService 的Client 端实现是IoConnector。</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> connector = NioSocketConnector()</span><br><span class="line"><span class="comment">// 设置链接超时时间</span></span><br><span class="line">connector.connectTimeoutMillis = <span class="number">15000</span></span><br><span class="line"><span class="comment">// 添加过滤器</span></span><br><span class="line">connector.filterChain.addLast(<span class="string">&quot;codec&quot;</span>,ProtocolCodecFilter(TextLineCodecFactory()))</span><br><span class="line">connector.setDefaultRemoteAddress(InetSocketAddress(ip, port))</span><br><span class="line"><span class="keyword">val</span> future = connector.connect()</span><br><span class="line">future.awaitUninterruptibly()<span class="comment">// 等待连接创建完成</span></span><br><span class="line"><span class="keyword">var</span> session = future.session<span class="comment">// 获得IoSession</span></span><br></pre></td></tr></table></figure><p>IoHandlerAdapter 和服务端一样，这里不做过多介绍。</p><p>最后贴上服务端代码：</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MinaServer</span> : <span class="type">IoHandlerAdapter</span>()&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">val</span> acceptor: NioSocketAcceptor</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">var</span> isConnected = <span class="literal">false</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">var</span> handler: Handler <span class="keyword">by</span> Delegates.notNull()</span><br><span class="line">    <span class="keyword">init</span> &#123;</span><br><span class="line">        <span class="comment">//创建一个非阻塞的service端的socket</span></span><br><span class="line">        acceptor = NioSocketAcceptor()</span><br><span class="line">        <span class="comment">//设置编解码器  ProtocolCodecFilter拦截器 网络传输需要将对象转换为字节流</span></span><br><span class="line">        acceptor.filterChain.addLast(<span class="string">&quot;codec&quot;</span>,</span><br><span class="line">                ProtocolCodecFilter(TextLineCodecFactory()))</span><br><span class="line">        <span class="comment">//设置读取数据的缓冲区大小</span></span><br><span class="line">        acceptor.sessionConfig.readBufferSize = <span class="number">2048</span></span><br><span class="line">        <span class="comment">//读写通道10秒内无操作进入空闲状态</span></span><br><span class="line">        acceptor.sessionConfig.setIdleTime(IdleStatus.BOTH_IDLE, <span class="number">10</span>)</span><br><span class="line">        handler = Handler()</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">fun</span> <span class="title">connect</span><span class="params">(port: <span class="type">Int</span>)</span></span>: MinaServer &#123;</span><br><span class="line">        <span class="keyword">if</span> (isConnected)</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">this</span></span><br><span class="line">        thread &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="comment">//注册回调 监听和客户端之间的消息</span></span><br><span class="line">                acceptor.handler = <span class="keyword">this</span></span><br><span class="line">                acceptor.isReuseAddress = <span class="literal">true</span></span><br><span class="line">                <span class="comment">//绑定端口</span></span><br><span class="line">                acceptor.bind(InetSocketAddress(port))</span><br><span class="line">                isConnected = <span class="literal">true</span></span><br><span class="line">                handler.post &#123;</span><br><span class="line">                    connectCallback?.onOpened()</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">catch</span> (e: Exception) &#123;</span><br><span class="line">                e.printStackTrace()</span><br><span class="line">                handler.post &#123;</span><br><span class="line">                    connectCallback?.onError(e)</span><br><span class="line">                &#125;</span><br><span class="line">                LogUtils.i(<span class="string">&quot;服务器连接异常&quot;</span>)</span><br><span class="line">                isConnected = <span class="literal">false</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">fun</span> <span class="title">sendText</span><span class="params">(message: <span class="type">String</span>)</span></span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (client <span class="keyword">in</span> acceptor.managedSessions.values)&#123;</span><br><span class="line">            <span class="keyword">var</span> ioBuffer = IoBuffer.allocate(message.toByteArray().size)</span><br><span class="line">            ioBuffer.put(message.toByteArray())</span><br><span class="line">            ioBuffer.flip()</span><br><span class="line">            client.write(ioBuffer)</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 向客户端发送消息后会调用此方法</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> session</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> message</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Throws(Exception::class)</span></span><br><span class="line">    <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">messageSent</span><span class="params">(session: <span class="type">IoSession</span>?, message: <span class="type">Any</span>?)</span></span> &#123;</span><br><span class="line">        <span class="keyword">super</span>.messageSent(session, message)</span><br><span class="line">        LogUtils.i(<span class="string">&quot;服务器发送消息成功&quot;</span>)</span><br><span class="line">        connectCallback?.onSendSuccess()</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 从端口接受消息，会响应此方法来对消息进行处理</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> session</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> message</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Throws(Exception::class)</span></span><br><span class="line">    <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">messageReceived</span><span class="params">(session: <span class="type">IoSession</span>?, message: <span class="type">Any</span>?)</span></span> &#123;</span><br><span class="line">        <span class="keyword">super</span>.messageReceived(session, message)</span><br><span class="line">        handler.post &#123;</span><br><span class="line">            connectCallback?.onGetMessage(message)</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">val</span> msg = message!!.toString()</span><br><span class="line">        LogUtils.i(<span class="string">&quot;服务器接收消息成功：<span class="variable">$msg</span>&quot;</span>)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 服务器与客户端创建连接</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> session</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Throws(Exception::class)</span></span><br><span class="line">    <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">sessionCreated</span><span class="params">(session: <span class="type">IoSession</span>?)</span></span> &#123;</span><br><span class="line">        <span class="keyword">super</span>.sessionCreated(session)</span><br><span class="line">        handler.post &#123;</span><br><span class="line">            connectCallback?.onConnected()</span><br><span class="line">        &#125;</span><br><span class="line">        LogUtils.i(<span class="string">&quot;服务器与客户端创建连接&quot;</span>)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 服务器与客户端连接打开</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> session</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Throws(Exception::class)</span></span><br><span class="line">    <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">sessionOpened</span><span class="params">(session: <span class="type">IoSession</span>?)</span></span> &#123;</span><br><span class="line">        <span class="keyword">super</span>.sessionOpened(session)</span><br><span class="line">        LogUtils.i(<span class="string">&quot;服务器与客户端连接打开&quot;</span>)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 关闭与客户端的连接时会调用此方法</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> session</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Throws(Exception::class)</span></span><br><span class="line">    <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">sessionClosed</span><span class="params">(session: <span class="type">IoSession</span>?)</span></span> &#123;</span><br><span class="line">        <span class="keyword">super</span>.sessionClosed(session)</span><br><span class="line">        handler.post &#123;</span><br><span class="line">            connectCallback?.onDisConnected()</span><br><span class="line">        &#125;</span><br><span class="line">        LogUtils.i(<span class="string">&quot;关闭与客户端的连接时会调用此方法&quot;</span>)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 服务器进入空闲状态</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> session</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> status</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Throws(Exception::class)</span></span><br><span class="line">    <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">sessionIdle</span><span class="params">(session: <span class="type">IoSession</span>?, status: <span class="type">IdleStatus</span>?)</span></span> &#123;</span><br><span class="line">        <span class="keyword">super</span>.sessionIdle(session, status)</span><br><span class="line">        LogUtils.i(<span class="string">&quot;服务器进入空闲状态&quot;</span>)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 异常</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> session</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> cause</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Throws(Exception::class)</span></span><br><span class="line">    <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">exceptionCaught</span><span class="params">(session: <span class="type">IoSession</span>?, cause: <span class="type">Throwable</span>)</span></span> &#123;</span><br><span class="line">        <span class="keyword">super</span>.exceptionCaught(session, cause)</span><br><span class="line">        handler.post &#123;</span><br><span class="line">            connectCallback?.onError(cause)</span><br><span class="line">        &#125;</span><br><span class="line">        LogUtils.i(<span class="string">&quot;服务器异常<span class="variable">$cause</span>&quot;</span>)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">var</span> connectCallback:ConnectCallback? = <span class="literal">null</span></span><br><span class="line">    <span class="function"><span class="keyword">fun</span> <span class="title">setConnectCallback</span><span class="params">(callback:<span class="type">ConnectCallback</span>)</span></span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.connectCallback = callback</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">interface</span> <span class="title class_">ConnectCallback</span>&#123;</span><br><span class="line">        <span class="function"><span class="keyword">fun</span> <span class="title">onSendSuccess</span><span class="params">()</span></span></span><br><span class="line">        <span class="function"><span class="keyword">fun</span> <span class="title">onGetMessage</span><span class="params">(message: <span class="type">Any</span>?)</span></span></span><br><span class="line">        <span class="function"><span class="keyword">fun</span> <span class="title">onOpened</span><span class="params">()</span></span></span><br><span class="line">        <span class="function"><span class="keyword">fun</span> <span class="title">onConnected</span><span class="params">()</span></span></span><br><span class="line">        <span class="function"><span class="keyword">fun</span> <span class="title">onDisConnected</span><span class="params">()</span></span></span><br><span class="line">        <span class="function"><span class="keyword">fun</span> <span class="title">onError</span><span class="params">(cause: <span class="type">Throwable</span>)</span></span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​</p><p>在服务端中的activity中使用：</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> mServer = MinaServer()</span><br><span class="line">        mServer</span><br><span class="line">                .connect(<span class="number">2333</span>)</span><br><span class="line">                .setConnectCallback(<span class="keyword">object</span> : MinaServer.ConnectCallback &#123;</span><br><span class="line">                    <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">onSendSuccess</span><span class="params">()</span></span> &#123;</span><br><span class="line">                        <span class="comment">//发送消息成功</span></span><br><span class="line">                    &#125;</span><br><span class="line"></span><br><span class="line">                    <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">onGetMessage</span><span class="params">(message: <span class="type">Any</span>?)</span></span> &#123;</span><br><span class="line">                        <span class="comment">//接收消息成功</span></span><br><span class="line">                        <span class="keyword">val</span> msg = message.toString()</span><br><span class="line">                    &#125;</span><br><span class="line"></span><br><span class="line">                    <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">onOpened</span><span class="params">()</span></span> &#123;</span><br><span class="line">                        </span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">onConnected</span><span class="params">()</span></span> &#123;</span><br><span class="line"></span><br><span class="line">                    &#125;</span><br><span class="line"></span><br><span class="line">                    <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">onDisConnected</span><span class="params">()</span></span> &#123;</span><br><span class="line"></span><br><span class="line">                    &#125;</span><br><span class="line"></span><br><span class="line">                    <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">onError</span><span class="params">(cause: <span class="type">Throwable</span>)</span></span> &#123;</span><br><span class="line">                        Toast.makeText(applicationContext, <span class="string">&quot;服务器异常&quot;</span> + cause.toString(), Toast.LENGTH_SHORT).show()</span><br><span class="line">                    &#125;</span><br><span class="line"></span><br><span class="line">                &#125;)</span><br></pre></td></tr></table></figure><p>再看客户端代码：</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br></pre></td><td class="code"><pre><span class="line">lass MinaClient : IoHandlerAdapter()&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">val</span> connector: NioSocketConnector</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">var</span> session: IoSession? = <span class="literal">null</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">var</span> isConnected = <span class="literal">false</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">var</span> handler:Handler <span class="keyword">by</span> Delegates.notNull()</span><br><span class="line">    <span class="keyword">init</span> &#123;</span><br><span class="line">        connector = NioSocketConnector()</span><br><span class="line">        <span class="comment">// 设置链接超时时间</span></span><br><span class="line">        connector.connectTimeoutMillis = <span class="number">15000</span></span><br><span class="line">        <span class="comment">// 添加过滤器</span></span><br><span class="line">        connector.filterChain.addLast(<span class="string">&quot;codec&quot;</span>,</span><br><span class="line">                ProtocolCodecFilter(TextLineCodecFactory()))</span><br><span class="line">        handler = Handler()</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">fun</span> <span class="title">connect</span><span class="params">(ip: <span class="type">String</span>, port: <span class="type">Int</span>)</span></span>: MinaClient &#123;</span><br><span class="line">        <span class="keyword">if</span> (isConnected)</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">this</span></span><br><span class="line">        thread &#123;</span><br><span class="line">            connector.handler = <span class="keyword">this</span></span><br><span class="line">            connector.setDefaultRemoteAddress(InetSocketAddress(ip, port))</span><br><span class="line">            <span class="comment">//开始连接</span></span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">val</span> future = connector.connect()</span><br><span class="line">                future.awaitUninterruptibly()<span class="comment">// 等待连接创建完成</span></span><br><span class="line">                session = future.session<span class="comment">// 获得session</span></span><br><span class="line">                isConnected = session != <span class="literal">null</span> &amp;&amp; session!!.isConnected</span><br><span class="line">            &#125; <span class="keyword">catch</span> (e: Exception) &#123;</span><br><span class="line">                e.printStackTrace()</span><br><span class="line">                handler.post &#123;</span><br><span class="line">                    connectCallback?.onError(e)</span><br><span class="line">                &#125;</span><br><span class="line">                println(<span class="string">&quot;客户端链接异常...&quot;</span>)</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">fun</span> <span class="title">disConnect</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (isConnected)&#123;</span><br><span class="line">            session?.closeOnFlush()</span><br><span class="line">            connector.dispose()</span><br><span class="line">        &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">            connectCallback?.onDisConnected()</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">fun</span> <span class="title">sendText</span><span class="params">(message: <span class="type">String</span>)</span></span>&#123;</span><br><span class="line">        <span class="keyword">var</span> ioBuffer = IoBuffer.allocate(message.toByteArray().size)</span><br><span class="line">        ioBuffer.put(message.toByteArray())</span><br><span class="line">        ioBuffer.flip()</span><br><span class="line">        session?.write(ioBuffer)</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 向服务端端发送消息后会调用此方法</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> session</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> message</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Throws(Exception::class)</span></span><br><span class="line">    <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">messageSent</span><span class="params">(session: <span class="type">IoSession</span>?, message: <span class="type">Any</span>?)</span></span> &#123;</span><br><span class="line">        <span class="keyword">super</span>.messageSent(session, message)</span><br><span class="line">        LogUtils.i(<span class="string">&quot;客户端发送消息成功&quot;</span>)</span><br><span class="line">        handler.post &#123;</span><br><span class="line">            connectCallback?.onSendSuccess()</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 从端口接受消息，会响应此方法来对消息进行处理</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> session</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> message</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Throws(Exception::class)</span></span><br><span class="line">    <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">messageReceived</span><span class="params">(session: <span class="type">IoSession</span>?, message: <span class="type">Any</span>?)</span></span> &#123;</span><br><span class="line">        <span class="keyword">super</span>.messageReceived(session, message)</span><br><span class="line">        LogUtils.i(<span class="string">&quot;客户端接收消息成功：&quot;</span>)</span><br><span class="line">        handler.post &#123;</span><br><span class="line">            connectCallback?.onGetMessage(message)</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 服务器与客户端创建连接</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> session</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Throws(Exception::class)</span></span><br><span class="line">    <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">sessionCreated</span><span class="params">(session: <span class="type">IoSession</span>?)</span></span> &#123;</span><br><span class="line">        <span class="keyword">super</span>.sessionCreated(session)</span><br><span class="line">        LogUtils.i(<span class="string">&quot;服务器与客户端创建连接&quot;</span>)</span><br><span class="line">        handler.post &#123;</span><br><span class="line">            connectCallback?.onConnected()</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 服务器与客户端连接打开</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> session</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Throws(Exception::class)</span></span><br><span class="line">    <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">sessionOpened</span><span class="params">(session: <span class="type">IoSession</span>?)</span></span> &#123;</span><br><span class="line">        <span class="keyword">super</span>.sessionOpened(session)</span><br><span class="line">        LogUtils.i(<span class="string">&quot;服务器与客户端连接打开&quot;</span>)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 关闭与客户端的连接时会调用此方法</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> session</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Throws(Exception::class)</span></span><br><span class="line">    <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">sessionClosed</span><span class="params">(session: <span class="type">IoSession</span>?)</span></span> &#123;</span><br><span class="line">        <span class="keyword">super</span>.sessionClosed(session)</span><br><span class="line">        LogUtils.i(<span class="string">&quot;关闭与客户端的连接时会调用此方法&quot;</span>)</span><br><span class="line">        isConnected = <span class="literal">false</span></span><br><span class="line">        handler.post &#123;</span><br><span class="line">            connectCallback?.onDisConnected()</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 客户端进入空闲状态</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> session</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> status</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Throws(Exception::class)</span></span><br><span class="line">    <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">sessionIdle</span><span class="params">(session: <span class="type">IoSession</span>?, status: <span class="type">IdleStatus</span>?)</span></span> &#123;</span><br><span class="line">        <span class="keyword">super</span>.sessionIdle(session, status)</span><br><span class="line">        LogUtils.i(<span class="string">&quot;客户端进入空闲状态&quot;</span>)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 异常</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> session</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> cause</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Throws(Exception::class)</span></span><br><span class="line">    <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">exceptionCaught</span><span class="params">(session: <span class="type">IoSession</span>?, cause: <span class="type">Throwable</span>)</span></span> &#123;</span><br><span class="line">        <span class="keyword">super</span>.exceptionCaught(session, cause)</span><br><span class="line">        LogUtils.i(<span class="string">&quot;客户端异常<span class="variable">$cause</span>&quot;</span>)</span><br><span class="line">        handler.post &#123;</span><br><span class="line">            connectCallback?.onError(cause)</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">var</span> connectCallback:ConnectCallback? = <span class="literal">null</span></span><br><span class="line">    <span class="function"><span class="keyword">fun</span> <span class="title">setConnectCallback</span><span class="params">(callback:<span class="type">ConnectCallback</span>)</span></span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.connectCallback = callback</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">interface</span> <span class="title class_">ConnectCallback</span>&#123;</span><br><span class="line">        <span class="function"><span class="keyword">fun</span> <span class="title">onSendSuccess</span><span class="params">()</span></span></span><br><span class="line">        <span class="function"><span class="keyword">fun</span> <span class="title">onGetMessage</span><span class="params">(message: <span class="type">Any</span>?)</span></span></span><br><span class="line">        <span class="function"><span class="keyword">fun</span> <span class="title">onConnected</span><span class="params">()</span></span></span><br><span class="line">        <span class="function"><span class="keyword">fun</span> <span class="title">onDisConnected</span><span class="params">()</span></span></span><br><span class="line">        <span class="function"><span class="keyword">fun</span> <span class="title">onError</span><span class="params">(cause: <span class="type">Throwable</span>)</span></span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>客户端的activity中使用：</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> mClient = MinaClient()</span><br><span class="line">      mClient</span><br><span class="line">              .connect(<span class="string">&quot;192.168.0.108&quot;</span>, <span class="number">2333</span>)</span><br><span class="line">              .setConnectCallback(<span class="keyword">object</span> : MinaClient.ConnectCallback &#123;</span><br><span class="line">                  <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">onGetMessage</span><span class="params">(message: <span class="type">Any</span>?)</span></span> &#123;</span><br><span class="line">                      <span class="keyword">val</span> msg = message.toString()</span><br><span class="line">                  &#125;</span><br><span class="line">                  <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">onConnected</span><span class="params">()</span></span> &#123;</span><br><span class="line">                      </span><br><span class="line">                  &#125;</span><br><span class="line"></span><br><span class="line">                  <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">onDisConnected</span><span class="params">()</span></span> &#123;</span><br><span class="line">                      Toast.makeText(applicationContext, <span class="string">&quot;断开连接成功&quot;</span>, Toast.LENGTH_SHORT).show()</span><br><span class="line">                  &#125;</span><br><span class="line">                  <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">onError</span><span class="params">(cause: <span class="type">Throwable</span>)</span></span> &#123;</span><br><span class="line">                      Toast.makeText(applicationContext, <span class="string">&quot;服务器异常&quot;</span> + cause.toString(), Toast.LENGTH_SHORT).show()</span><br><span class="line">                  &#125;</span><br><span class="line"></span><br><span class="line">                  <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">onSendSuccess</span><span class="params">()</span></span> &#123;</span><br><span class="line"></span><br><span class="line">                  &#125;</span><br><span class="line"></span><br><span class="line">              &#125;)</span><br></pre></td></tr></table></figure><p>界面布局比较简单，就是一个recyclerview+几个button，如果觉得我讲得不够清楚T^T，可以到github上查看源码：<a href="https://github.com/lingxiaopua/MinaSimple">minaSimple</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;工欲善其事，必先利其器，从互联网诞生到现在，基本上所有的程序都是网络程序，很少有单机版的程序了。 而网络编程的本质是两个设备之间的数据交换，当然，在计算机网络中，设备主要指计算机。我们现在进行网络编程，基本上都是使用已经封装好的框架，毕竟自己实现维护一套网络编程框架，费时费</summary>
      
    
    
    
    <category term="java" scheme="http://example.com/categories/java/"/>
    
    
  </entry>
  
  <entry>
    <title>视频开发基础篇</title>
    <link href="http://example.com/2018/12/14/%E8%A7%86%E9%A2%91%E5%BC%80%E5%8F%91%E5%9F%BA%E7%A1%80%E7%AF%87/"/>
    <id>http://example.com/2018/12/14/%E8%A7%86%E9%A2%91%E5%BC%80%E5%8F%91%E5%9F%BA%E7%A1%80%E7%AF%87/</id>
    <published>2018-12-14T02:11:44.000Z</published>
    <updated>2025-09-15T02:12:48.413Z</updated>
    
    <content type="html"><![CDATA[<h3 id="视频开发基础篇"><a href="#视频开发基础篇" class="headerlink" title="视频开发基础篇"></a>视频开发基础篇</h3><p>在进行视频开发之前，如果你对颜色编码格式不是很熟悉，建议先阅读一下这篇文章：<a href="https://blog.csdn.net/junzia/article/details/76315120">yuv和rgb的相关知识</a>。有一个大概了解之后，在看下面的视频相关的基础知识。</p><p>封装格式的辨认很简单，大多数情况下，拓展名就是封装格式的名字。比如一个文件“金刚狼.avi”，那它就是属于avi的封装格式，“钢之炼金术师.Mkv”，那它就属于mkv封装格式， “喜羊羊与灰太狼.rmvb”就是rmvb封装格式。简而言之，视频文件后缀名就是封装格式的名字。不过wmv和rmvb就比较特殊，后面编码器部分再说。封装格式就是酒瓶。酒瓶就不影响酒的口感，同样，封装格式一样不影响视频画质。它只负责把内部的视频轨和音频轨集成在一起，只起到一个文件夹（或者压缩包）的作用，并没有对视频轨和音频轨造成影响。</p><h4 id="几种主流媒体格式"><a href="#几种主流媒体格式" class="headerlink" title="几种主流媒体格式"></a>几种主流媒体格式</h4><ol><li><strong>AVI(Audio Video Interactive)</strong> 比较早的AVI是Microsoft开发的。其含义是Audio Video Interactive，就是把视频和音频编码混合在一起存储。<br>AVI也是最长寿的格式，已存在10余年了，虽然发布过改版（V2.0于1996年发布），但已显老态。AVI格式上限制比较多，只能有一个视频轨道和一个音频轨道（现在有非标准插件可加入最多两个音频轨道），还可以有一些附加轨道，如文字等。AVI格式不提供任何控制功能。扩展名：avi<br>总结：只能封装一条视频和一条音频，不能封装字幕，没有流媒体功能（就是不能使用它进行在线播放的意思）</li><li>**WMV（Windows Media Video）**是微软公司开发的一组数字视频编解码格式的通称，ASF（Advanced Systems Format）是其封装格式。 ASF封装的WMV档具有“数字版权保护”功能。扩展名:wmv&#x2F;asf、wmvhd 其音频编码采用WMA，视频编码采用WMV,需要版权支持且机顶盒支持的不多。</li><li>**MPEG（Moving Picture Experts Group）**是一个国际标准化组织（ISO）认可的媒体封装形式，受到大部份机器的支持。其存储方式多样，可以适应不同的应用环境。MPEG-4档的档容器格式在Part 1（mux）、14（asp）、15（avc）等中规定。MPEG的控制功能丰富，可以有多个视频（即角度）、音轨、字幕（位图字幕）等等。<ol><li>MPEG1是最早出现的，VCD这个具体的格式最早是从日本来的并遵从MPEG1规格</li><li>MPEG2：其代表是DVD。一般为480P（640*480）。DVD当时都符合这个标准。Xvid 和H.264同属于MPEG4，MPEG-2还可能称作MPEG2-HD，MPEG2-TS，其实在ITU-T的命名规范中被称之为H.262</li><li>H.264有两个名称，一个是沿用ITU-T组织的H.26x名称，叫“H.264”，另一个则是“MPEG-4AVC（MPEG4第10部分）”，AVC意为高级视频编码。</li><li>H264的数据压缩率比当前DVD系统中使用的MPEG-2高2-3倍，比MPEG-4高1.5-2倍。x264编码，是H.264编码的一个开源分支，它符合H.264标准，其功能在于编码(encoding)，而不作为解码器(decoder)使用，264编码最大的特点在于注重实用，它在不明显降低编码性能的前提下，努力降低编码的计算复杂度，X264对于H.264编码中的一些复杂编码特性做了折衷处理，其压缩视频体积小于Xvid(MPEG4)编码视频</li><li>mp4格式是H.264编码指定使用的标准封装格式，3GP是MP4格式的一种简化版本，减少了储存空间和较低的频宽需求，让手机上有限的储存空间可以使用可以封装多种多条编码的视频和音频，也可以封装字幕，但没有流媒体功能，可以做成分片式进行流式播放。</li></ol></li><li><strong>Matroska</strong> 是一种新的多媒体封装格式，这个封装格式可把多种不同编码的视频及16条或以上不同格式的音频和语言不同的字幕封装到一个Matroska Media档内。它也是其中一种开放源代码的多媒体封装格式。Matroska同时还可以提供非常好的交互功能，而且比MPEG的方便、强大。扩展名:mkv</li><li>**Real Video或者称Real Media（RM）**档是由RealNetworks开发的一种档容器。它通常只能容纳Real Video和Real Audio编码的媒体。该档带有一定的交互功能，允许编写脚本以控制播放。RM，尤其是可变比特率的RMVB格式，体积很小，非常受到网络下载者的欢迎。扩展名：rm&#x2F;rmvb</li><li><strong>QuickTime File Format</strong> 是由苹果公司开发的容器。1998年2月11日，国际标准组织（ISO）认可QuickTime文件格式作为MPEG-4标准的基础。QT可存储的内容相当丰富，除了视频、音频以外还可支持图片、文字（文本字幕）等。扩展名：mov、qt</li><li><strong>Ogg Media</strong> 是一个完全开放性的多媒体系统计划，OGM（Ogg Media File）是其容器格式。OGM可以支持多视频、音频、字幕（文本字幕）等多种轨道。扩展名：ogg</li><li><strong>MOD</strong> 格式是JVC生产的硬盘摄录机所采用的存储格式名称</li><li><strong>MKV</strong> 和mp4差不多，但有流媒体功能，可以使用它进行在线播放，还可以使用它封装rv（rm&#x2F;rmvb）编码</li></ol><p>另外：<br>视频封装格式是MP4，MKV，或者其它，它们都是：视频流，音频流，字幕完全用自己独立的轨道，并且可以是外挂字幕。<br>字幕后缀名为str，ass，ssa等。这些字幕有自己的特效，什么时候以怎样的形式出现哪些文字，都是由写好的代码控制的。</p><h4 id="编码格式"><a href="#编码格式" class="headerlink" title="编码格式"></a>编码格式</h4><p>就是指通过特定的压缩技术，将某个视频格式的文件转换成另 一种视频格式文件的方式。目前视频流传输中最为重要的编解码标准有国际电联的H.264，运动静止图像专家组的M-JPEG和国际标准化组织运动图像专家 组的MPEG系列标准，此外在互联网上被广泛应用的还有Real-Networks的RealVideo、微软公司的WMV以及Apple公司的 QuickTime等。</p><p>在这里我们主要讲一下主流的H.264编码。H.264 使图像压缩技术上升到了一个更高的阶段，能够在较低带宽上提供高质量的图像传输，该优点非常适合国内运营商用户量大、接入网&#x2F;骨干网带宽相对有限的状况。 在同等的画质下，H.264 比上一代编码标准MPEG2 平均节约64％的传输码流，而比MPEG4 ASP 要平均节约39％的传输码流。如果要具体讲解H.264相关的知识，又是一大篇，而且博主知识有限，不能很好的说清楚，所以这里就只能贴上一篇参考文章了：<a href="https://www.cnblogs.com/lidabo/p/6857616.html">如何实现1080P延迟低于500ms的实时超清直播传输技术</a></p><h4 id="视频码率"><a href="#视频码率" class="headerlink" title="视频码率"></a>视频码率</h4><p>视频码率是指视频文件在单位时间内使用的数据流量，也叫码流率。码率越大，说明单位时间内取样率越大，数据流精度就越高，这样表现出来的的效果就是：视频画面更清晰画质更高。这里要和视频帧率区分开。</p><h4 id="视频帧率"><a href="#视频帧率" class="headerlink" title="视频帧率"></a>视频帧率</h4><p>通常说一个视频的25帧，指的就是这个视频帧率，即1秒中会显示25帧；视频帧率影响的是画面流畅感，也就是说视频帧率超高，表现出来的效果就是：画面越显得流畅。你也可以这样理解，假设1秒只显1帧，那么一段视频看起来，就是有很明显的卡顿感，不流畅不连惯。当然视频帧率越高，意味着画面越多，也就相应的，这个视频文件的大小也会随之增加，占用存储空间也就增大了。</p><h4 id="后话"><a href="#后话" class="headerlink" title="后话"></a>后话</h4><p>封装格式和编码的关系，就是和酒与酒瓶的关系差不多，而播放器就是开酒器。为了可以喝更好的酒，最好先了解酒是什么酒，酒瓶是什么酒瓶，怎么使用开酒器开酒瓶。</p><p>码率或者可以比喻做酒的原料。对于同一个酒瓶和做同一种酒的情况下，如果原料太少，又要要求用酒把酒瓶灌满，此时只好兑水了，酒的品质就会变差了。然而，如果原料太多，又会造成原料浪费。所以要做好酒，我们就需要充足的原料。同样道理，压制一个视频，充足的码率是非常需要的。如果码率过高，画质提升不上去的同时还使视频体积过大。如果码率不足，视频容易出各种各样的问题，导致压崩。当然，码率不是画质的唯一指标，压制的时候的各种参数设置，各种滤镜也是非常重要的。如果码率给得足够高，理论上各编码的画质是一样的，不过体积完全不一样。</p><p>本篇文章只做一个大概介绍，因为每一个点往深入讲都能讲很长时间，视频开发中的每一个门类，都可以深挖，衍生出一个又一个充满技术挑战的话题。音视频的路很长，更多的还是要靠自己去学习和实践，希望我的文章能对你产生一点点意义。</p><p>参考链接：<a href="https://blog.csdn.net/andyhuabing/article/details/8063298">封装格式和编码格式</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h3 id=&quot;视频开发基础篇&quot;&gt;&lt;a href=&quot;#视频开发基础篇&quot; class=&quot;headerlink&quot; title=&quot;视频开发基础篇&quot;&gt;&lt;/a&gt;视频开发基础篇&lt;/h3&gt;&lt;p&gt;在进行视频开发之前，如果你对颜色编码格式不是很熟悉，建议先阅读一下这篇文章：&lt;a href=&quot;htt</summary>
      
    
    
    
    <category term="视频开发" scheme="http://example.com/categories/%E8%A7%86%E9%A2%91%E5%BC%80%E5%8F%91/"/>
    
    
  </entry>
  
  <entry>
    <title>ffmpeg-编译</title>
    <link href="http://example.com/2018/08/29/ffmpeg-%E7%BC%96%E8%AF%91/"/>
    <id>http://example.com/2018/08/29/ffmpeg-%E7%BC%96%E8%AF%91/</id>
    <published>2018-08-29T02:09:45.000Z</published>
    <updated>2025-09-15T02:10:48.306Z</updated>
    
    <content type="html"><![CDATA[<p>记一次编译ffmpeg过程：</p><h4 id="环境准备"><a href="#环境准备" class="headerlink" title="环境准备"></a>环境准备</h4><ol><li>ubuntu16.04 我是64位，所以还要额外安装32位依赖库</li><li>ndk环境</li></ol><p>首先去官网下载最新的源码：<a href="!http://ffmpeg.org/download.html">ffmpeg4.0.2</a></p><p>随便解压到一个目录，先运行一次命令.&#x2F;configure，如果没错可以进行下一步操作，编写脚本</p><p>新建一个xxx.sh文件，往里面写入如下内容：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">#</span><span class="language-bash">!/bin/bash</span></span><br><span class="line"></span><br><span class="line">NDK=/opt/android-ndk-r14b #ndk目录</span><br><span class="line">SYSROOT=$NDK/platforms/android-14/arch-arm/</span><br><span class="line">TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64</span><br><span class="line">CPU=armv7-a  #需要编译哪种架构的so库</span><br><span class="line">PREFIX=$(pwd)/android/$CPU</span><br><span class="line">ADDI_CFLAGS=&quot;-marm&quot;</span><br><span class="line">OPTIMIZE_CFLAGS=&quot;-mfloat-abi=softfp -mfpu=vfp -marm -march=$CPU &quot;</span><br><span class="line"></span><br><span class="line">function build_android</span><br><span class="line">&#123;</span><br><span class="line">./configure \</span><br><span class="line">--prefix=$PREFIX \  #规定编译文件在哪里生成</span><br><span class="line">--enable-neon \</span><br><span class="line">--enable-hwaccels \</span><br><span class="line">--enable-shared \ #生成动态库（共享库）</span><br><span class="line">--target-os=android \ #目标系统</span><br><span class="line">--enable-jni \</span><br><span class="line">--enable-mediacodec \ #硬解码支持</span><br><span class="line">--enable-decoder=h264_mediacodec \ </span><br><span class="line">--disable-static \ #禁止生成静态库</span><br><span class="line">--disable-doc \</span><br><span class="line">--enable-ffmpeg \</span><br><span class="line">--disable-ffplay \</span><br><span class="line">--disable-ffprobe \</span><br><span class="line">--enable-avdevice \</span><br><span class="line">--disable-doc \</span><br><span class="line">--disable-symver \</span><br><span class="line">--cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \  #交叉编译链</span><br><span class="line">--arch=arm \ #目标平台架构</span><br><span class="line">--cpu=armv7-a \</span><br><span class="line">--enable-cross-compile \  #启用交叉编译方式</span><br><span class="line">--sysroot=$SYSROOT \</span><br><span class="line">--extra-cflags=&quot;-Os -fpic $ADDI_CFLAGS&quot; \</span><br><span class="line">--extra-ldflags=&quot;$ADDI_LDFLAGS&quot; \</span><br><span class="line"><span class="meta prompt_">$</span><span class="language-bash">ADDITIONAL_CONFIGURE_FLAG</span></span><br><span class="line">&#125;</span><br><span class="line">build_android</span><br></pre></td></tr></table></figure><p>放入解压之后的根目录，我这里命名为build.sh。然后在终端执行命令 bash build.sh。我第一次编译提示如下错误jni not found：</p><p><img src="http://oqz3bypff.bkt.clouddn.com/ffmpeg_build_ndk_error.jpg"></p><p>找了搜索好几天无果，后面拿着log结合别的文章看了又看，觉得可能是ndk版本的问题，于是换成r14，结果编译成功了。</p><p>ndk-r16版本：</p><p><img src="http://oqz3bypff.bkt.clouddn.com/ndk_version_r16.png"></p><p>ndk-r14版本：</p><p><img src="http://oqz3bypff.bkt.clouddn.com/ndk_version_r14.png"></p><p>主要是缺失了include文件夹，include文件夹里有个jni.h头文件，所以会报错jni not found。</p><p>然后再次运行脚本，没有错误，就执行下面两个命令：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">make </span><br><span class="line">make install</span><br></pre></td></tr></table></figure><p>编译完成后，在当前目录下的android&#x2F;arm／下会有<strong>inclue</strong> 和 <strong>lib</strong>两文件夹   inclue 存放的是相关模块的.h文件，lib 存放的就是.so文件   </p><p>最后放上ffmpeg编译好的github地址：<a href="!https://github.com/lingxiaopua/ffmpeg_android">ffmpeg_build_demo</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;记一次编译ffmpeg过程：&lt;/p&gt;
&lt;h4 id=&quot;环境准备&quot;&gt;&lt;a href=&quot;#环境准备&quot; class=&quot;headerlink&quot; title=&quot;环境准备&quot;&gt;&lt;/a&gt;环境准备&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;ubuntu16.04 我是64位，所以还要额外安装32位依赖库&lt;/</summary>
      
    
    
    
    <category term="编译" scheme="http://example.com/categories/%E7%BC%96%E8%AF%91/"/>
    
    
  </entry>
  
</feed>
