<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>王明的技术博客</title>
  
  
  <link href="https://wangmingco.github.io/atom.xml" rel="self"/>
  
  <link href="https://wangmingco.github.io/"/>
  <updated>2025-12-19T01:50:58.670Z</updated>
  <id>https://wangmingco.github.io/</id>
  
  <author>
    <name>王明</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>svg</title>
    <link href="https://wangmingco.github.io/3000/01/01/html/svg/"/>
    <id>https://wangmingco.github.io/3000/01/01/html/svg/</id>
    <published>3000-01-01T00:00:00.000Z</published>
    <updated>2025-12-19T01:50:58.670Z</updated>
    
    <content type="html"><![CDATA[<script src="/assets/asciinema-player.js"></script><link rel="stylesheet" type="text/css" href="/assets/asciinema-player.css" /><ul><li><a href="/html/svg/agent.svg" target="_blank">agent</a></li><li><a href="/html/svg/AQS数据结构.svg" target="_blank">AQS数据结构</a></li><li><a href="/html/svg/ArrayBlockingQueue.svg" target="_blank">ArrayBlockingQueue</a></li><li><a href="/html/svg/CountDownLatch.svg" target="_blank">CountDownLatch</a></li><li><a href="/html/svg/DirectBuffer-GC源码(V144).svg" target="_blank">DirectBuffer-GC源码(V144)</a></li><li><a href="/html/svg/DMA.svg" target="_blank">DMA</a></li><li><a href="/html/svg/Git.svg" target="_blank">Git</a></li><li><a href="/html/svg/hashmap-put.svg" target="_blank">hashmap-put</a></li><li><a href="/html/svg/httpclient.svg" target="_blank">httpclient</a></li><li><a href="/html/svg/HTTPS.svg" target="_blank">HTTPS</a></li><li><a href="/html/svg/Java编码.svg" target="_blank">Java编码</a></li><li><a href="/html/svg/JDK8-nio.svg" target="_blank">JDK8-nio</a></li><li><a href="/html/svg/jndi-hack.svg" target="_blank">jndi-hack</a></li><li><a href="/html/svg/LinkedBlockingDeque.svg" target="_blank">LinkedBlockingDeque</a></li><li><a href="/html/svg/LinkedBlockingQueue.svg" target="_blank">LinkedBlockingQueue</a></li><li><a href="/html/svg/loadclass.svg" target="_blank">loadclass</a></li><li><a href="/html/svg/MappedByteBuffer.svg" target="_blank">MappedByteBuffer</a></li><li><a href="/html/svg/MySQL-InnoDB架构.svg" target="_blank">MySQL-InnoDB架构</a></li><li><a href="/html/svg/MySQL-事务.svg" target="_blank">MySQL-事务</a></li><li><a href="/html/svg/MySQL-大纲.svg" target="_blank">MySQL-大纲</a></li><li><a href="/html/svg/MySQL-死锁分析.svg" target="_blank">MySQL-死锁分析</a></li><li><a href="/html/svg/MySQL-索引.svg" target="_blank">MySQL-索引</a></li><li><a href="/html/svg/MySQL-锁.svg" target="_blank">MySQL-锁</a></li><li><a href="/html/svg/Netty-ByteBufAllocator.svg" target="_blank">Netty-ByteBufAllocator</a></li><li><a href="/html/svg/Netty-IO模型.svg" target="_blank">Netty-IO模型</a></li><li><a href="/html/svg/Netty-架构.svg" target="_blank">Netty-架构</a></li><li><a href="/html/svg/NIO.svg" target="_blank">NIO</a></li><li><a href="/html/svg/nsb.svg" target="_blank">nsb</a></li><li><a href="/html/svg/PipedXXXStream.svg" target="_blank">PipedXXXStream</a></li><li><a href="/html/svg/ReentrantLock.svg" target="_blank">ReentrantLock</a></li><li><a href="/html/svg/reflections.svg" target="_blank">reflections</a></li><li><a href="/html/svg/RocketMQ.svg" target="_blank">RocketMQ</a></li><li><a href="/html/svg/SLF4J.svg" target="_blank">SLF4J</a></li><li><a href="/html/svg/Spring-AOP.svg" target="_blank">Spring-AOP</a></li><li><a href="/html/svg/Spring-BeanFactory.svg" target="_blank">Spring-BeanFactory</a></li><li><a href="/html/svg/Spring-Beans.svg" target="_blank">Spring-Beans</a></li><li><a href="/html/svg/Spring-事务.svg" target="_blank">Spring-事务</a></li><li><a href="/html/svg/Spring-生命周期.svg" target="_blank">Spring-生命周期</a></li><li><a href="/html/svg/springboot-2.4.2.svg" target="_blank">springboot-2</a></li><li><a href="/html/svg/ssh.svg" target="_blank">ssh</a></li><li><a href="/html/svg/TCO-源码.svg" target="_blank">TCO-源码</a></li><li><a href="/html/svg/TCP-流程.svg" target="_blank">TCP-流程</a></li><li><a href="/html/svg/ThreadLocal.svg" target="_blank">ThreadLocal</a></li><li><a href="/html/svg/tomcat-coyote.svg" target="_blank">tomcat-coyote</a></li><li><a href="/html/svg/tomcat-mapper.svg" target="_blank">tomcat-mapper</a></li><li><a href="/html/svg/tomcat.svg" target="_blank">tomcat</a></li><li><a href="/html/svg/划扣架构.svg" target="_blank">划扣架构</a></li><li><a href="/html/svg/子网掩码.svg" target="_blank">子网掩码</a></li><li><a href="/html/svg/线程-AtomicInteger.svg" target="_blank">线程-AtomicInteger</a></li><li><a href="/html/svg/线程-cpp-内联-汇编.svg" target="_blank">线程-cpp-内联-汇编</a></li><li><a href="/html/svg/线程-LockSupport.svg" target="_blank">线程-LockSupport</a></li><li><a href="/html/svg/线程-sleep-interrupt.svg" target="_blank">线程-sleep-interrupt</a></li><li><a href="/html/svg/线程-wait-notify.svg" target="_blank">线程-wait-notify</a></li><li><a href="/html/svg/线程创建过程.svg" target="_blank">线程创建过程</a></li><li><a href="/html/svg/线程池.svg" target="_blank">线程池</a></li><li><a href="/html/svg/线程状态.svg" target="_blank">线程状态</a></li><li><a href="/html/svg/跨域.svg" target="_blank">跨域</a></li><li><a href="/html/svg/锁膨胀.svg" target="_blank">锁膨胀</a></li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;script src=&quot;/assets/asciinema-player.js&quot;&gt;&lt;/script&gt;&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;/assets/asciinema-player.css&quot; /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a</summary>
      
    
    
    
    
  </entry>
  
  <entry>
    <title>Java 中执行sql</title>
    <link href="https://wangmingco.github.io/2024/05/15/%E5%BC%80%E6%BA%90/java%20sql/"/>
    <id>https://wangmingco.github.io/2024/05/15/%E5%BC%80%E6%BA%90/java%20sql/</id>
    <published>2024-05-15T00:00:00.000Z</published>
    <updated>2025-12-19T01:50:58.670Z</updated>
    
    <content type="html"><![CDATA[<script src="/assets/asciinema-player.js"></script><link rel="stylesheet" type="text/css" href="/assets/asciinema-player.css" /><p>在字符串模板中拼装SQL，字符串支持变量表达式（可以是变量，可以进行运算，可以调用函数），dea 自动补全SQL</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 查询SQL</span></span><br><span class="line"><span class="type">String</span> <span class="variable">selectSql</span> <span class="operator">=</span> sql<span class="string">&quot;</span></span><br><span class="line"><span class="string">select * from $&#123;table&#125; where name = $&#123;name&#125;</span></span><br><span class="line"><span class="string">&quot;</span>;</span><br><span class="line"><span class="comment">// 执行SQL</span></span><br><span class="line">Data&lt;User&gt; data = jdbc.selectSql(sql, User.class);</span><br><span class="line"><span class="comment">//获取结果</span></span><br><span class="line"><span class="type">boolean</span> <span class="variable">isEmpty</span> <span class="operator">=</span> data.isEmpty();</span><br><span class="line"><span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> data.getOne(); <span class="comment">// 可能为null</span></span><br><span class="line">List&lt;User&gt; list = data.getList() <span class="comment">// 不为null，但是size可为0</span></span><br><span class="line">Map&lt;Long, User&gt; map = data.getMap(user -&gt; user.getId);</span><br><span class="line"><span class="type">String</span> <span class="variable">json</span> <span class="operator">=</span> data.toJson();</span><br><span class="line"><span class="comment">// 对Data进行Stream支持</span></span><br><span class="line"><span class="type">Data</span> <span class="variable">data</span> <span class="operator">=</span> data.filter(user -&gt; user.age &gt; <span class="number">10</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 插入SQL</span></span><br><span class="line"><span class="type">String</span> <span class="variable">insertOneSql</span> <span class="operator">=</span> sql<span class="string">&quot;</span></span><br><span class="line"><span class="string">insert into $&#123;table&#125;(id, name, age) values($&#123;user.id&#125;, $&#123;user.name&#125;, $&#123;user.age&#125;)</span></span><br><span class="line"><span class="string">&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">String</span> <span class="variable">insertListSql</span> <span class="operator">=</span> sql<span class="string">&quot;</span></span><br><span class="line"><span class="string">insert into $&#123;table&#125;(id, name, age) values $&#123;</span></span><br><span class="line"><span class="string">for(User user in list) &#123;</span></span><br><span class="line"><span class="string"> sql(join=&quot;</span>,<span class="string">&quot;)&quot;</span> ($&#123;user.id&#125;, $&#123;user.name&#125;, $&#123;user.age&#125;) <span class="string">&quot;</span></span><br><span class="line"><span class="string">&#125;</span></span><br><span class="line"><span class="string">&#125;</span></span><br><span class="line"><span class="string">&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">Data</span> <span class="variable">data</span> <span class="operator">=</span> jdbc.update(insertOneSql);</span><br><span class="line"></span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;script src=&quot;/assets/asciinema-player.js&quot;&gt;&lt;/script&gt;&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;/assets/asciinema-player.css&quot; /&gt;
&lt;p&gt;在字符串模板中拼</summary>
      
    
    
    
    <category term="开源" scheme="https://wangmingco.github.io/categories/%E5%BC%80%E6%BA%90/"/>
    
    
  </entry>
  
  <entry>
    <title>Vue 页面 内嵌 Grafana 页面</title>
    <link href="https://wangmingco.github.io/2024/05/10/%E5%BC%80%E6%BA%90/Vue%20%E9%A1%B5%E9%9D%A2%20%E5%86%85%E5%B5%8C%20Grafana%20%E9%A1%B5%E9%9D%A2/"/>
    <id>https://wangmingco.github.io/2024/05/10/%E5%BC%80%E6%BA%90/Vue%20%E9%A1%B5%E9%9D%A2%20%E5%86%85%E5%B5%8C%20Grafana%20%E9%A1%B5%E9%9D%A2/</id>
    <published>2024-05-10T00:00:00.000Z</published>
    <updated>2025-12-19T01:50:58.670Z</updated>
    
    <content type="html"><![CDATA[<script src="/assets/asciinema-player.js"></script><link rel="stylesheet" type="text/css" href="/assets/asciinema-player.css" /><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><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br></pre></td><td class="code"><pre><span class="line">&lt;template&gt;</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">      &lt;div&gt;</span><br><span class="line">        &lt;iframe id=&quot;iframeId&quot; :src=&quot;uri&quot; :width=&quot;width&quot; :height=&quot;height&quot; allowfullscreen=&quot;true&quot; webkitallowfullscreen=&quot;true&quot; mozallowfullscreen=&quot;true&quot; oallowfullscreen=&quot;true&quot; msallowfullscreen=&quot;true&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;</span><br><span class="line">        &lt;el-button id=&quot;btnId&quot; class=&quot;el-icon-full-screen&quot; size=&quot;mini&quot;&gt;&lt;/el-button&gt;</span><br><span class="line">    &lt;/div&gt;</span><br><span class="line">    &lt;/div&gt;</span><br><span class="line">  &lt;/template&gt;</span><br><span class="line">  </span><br><span class="line">  &lt;script&gt;</span><br><span class="line">  import Cookies from &#x27;js-cookie&#x27;</span><br><span class="line">  export default &#123;</span><br><span class="line">    name: &#x27;Grafana&#x27;,</span><br><span class="line">    props: &#123;</span><br><span class="line">        clusterId: &#123;</span><br><span class="line">          type: Number,</span><br><span class="line">          default: 1</span><br><span class="line">        &#125;,</span><br><span class="line">        url: &#123;</span><br><span class="line">            type: String,</span><br><span class="line">            default: &#x27;/grafana/d/xxx/xxxx-xxxx-xxx-xxx-xxx?orgId=1&amp;kiosk&amp;refresh=15s&#x27;</span><br><span class="line">        &#125;,</span><br><span class="line">        width: &#123;</span><br><span class="line">            type: String,</span><br><span class="line">        &#125;,</span><br><span class="line">        height: &#123;</span><br><span class="line">            type: String,</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    watch: &#123;</span><br><span class="line">        clusterId(n) &#123;</span><br><span class="line">            this.setCookie()</span><br><span class="line">        &#125;,</span><br><span class="line">        url(n) &#123;</span><br><span class="line">            this.setCookie()</span><br><span class="line">            this.uri = n</span><br><span class="line">        &#125;,</span><br><span class="line">        width(n) &#123;</span><br><span class="line">            // console.log(&#x27;width&#x27;, n)</span><br><span class="line">        &#125;,</span><br><span class="line">        height(n) &#123;</span><br><span class="line">            // console.log(&#x27;height&#x27;, n)</span><br><span class="line">        &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">    data() &#123;</span><br><span class="line">      return &#123;</span><br><span class="line">        uri: &#x27;&#x27;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    mounted() &#123;</span><br><span class="line">        this.setCookie()</span><br><span class="line">        this.uri = this.url</span><br><span class="line"></span><br><span class="line">        document.getElementById(&#x27;btnId&#x27;).addEventListener(&#x27;click&#x27;, function() &#123;</span><br><span class="line">        // 获取iframe元素</span><br><span class="line">        var iframe = document.getElementById(&#x27;iframeId&#x27;);</span><br><span class="line"></span><br><span class="line">        // 请求全屏</span><br><span class="line">        if (iframe.requestFullscreen) &#123;</span><br><span class="line">          iframe.requestFullscreen();</span><br><span class="line">        &#125; else if (iframe.webkitRequestFullscreen) &#123; /* Chrome, Safari 和 Opera */</span><br><span class="line">          iframe.webkitRequestFullscreen();</span><br><span class="line">        &#125; else if (iframe.msRequestFullscreen) &#123; /* IE/Edge */</span><br><span class="line">          iframe.msRequestFullscreen();</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;,</span><br><span class="line">    methods: &#123;</span><br><span class="line">      setCookie() &#123;</span><br><span class="line">        let seconds = 60 * 15; // 15分钟过期</span><br><span class="line">        let expires = new Date(new Date() * 1 + seconds * 1000);</span><br><span class="line">        Cookies.set(&#x27;token&#x27;, sessionStorage.getItem(&#x27;token&#x27;), &#123; expires: expires, sameSite: &#x27;Strict&#x27; &#125;);</span><br><span class="line">        Cookies.set(&#x27;clusterId&#x27;, this.clusterId, &#123; expires: expires, sameSite: &#x27;Strict&#x27; &#125;);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  &lt;/script&gt;</span><br><span class="line">  </span><br><span class="line">  &lt;style lang=&quot;stylus&quot; scoped&gt;</span><br><span class="line">  #btnId &#123;</span><br><span class="line">    position: absolute;</span><br><span class="line">    top: 10px; /* 根据需要调整 */</span><br><span class="line">    right: 10px; /* 根据需要调整 */</span><br><span class="line">    z-index: 1000; /* 确保按钮在iframe之上 */</span><br><span class="line">&#125;</span><br><span class="line">  &lt;/style&gt;</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;script src=&quot;/assets/asciinema-player.js&quot;&gt;&lt;/script&gt;&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;/assets/asciinema-player.css&quot; /&gt;
&lt;p&gt;前端页面&lt;/p&gt;</summary>
      
    
    
    
    <category term="开源" scheme="https://wangmingco.github.io/categories/%E5%BC%80%E6%BA%90/"/>
    
    
  </entry>
  
  <entry>
    <title>Soot 基础对象 [译]</title>
    <link href="https://wangmingco.github.io/2022/08/05/%E7%BF%BB%E8%AF%91/soot/1%20Fundamental%20Soot%20objects/"/>
    <id>https://wangmingco.github.io/2022/08/05/%E7%BF%BB%E8%AF%91/soot/1%20Fundamental%20Soot%20objects/</id>
    <published>2022-08-05T10:21:00.000Z</published>
    <updated>2025-12-19T01:50:58.678Z</updated>
    
    <content type="html"><![CDATA[<script src="/assets/asciinema-player.js"></script><link rel="stylesheet" type="text/css" href="/assets/asciinema-player.css" /><h2 id="Fundamental-Soot-objects"><a href="#Fundamental-Soot-objects" class="headerlink" title="Fundamental Soot objects"></a>Fundamental Soot objects</h2><p><img src="https://github.com/soot-oss/soot/wiki/Fundamental-Soot-objects" alt="Fundamental Soot objects"></p><p>Soot的类继承结构非常庞大和复杂，本节为读者介绍一些在Soot开发中最重要的类。</p><p>我们会详细介绍如下几个类的概念<code>Body</code>, <code>Unit</code>, <code>Local</code>, <code>Value</code>, <code>UnitBox</code> 和 <code>ValueBox</code></p><h3 id="Body"><a href="#Body" class="headerlink" title="Body"></a>Body</h3><p>在教程<a href="">Creating a class from scratch</a> 中已经提到过<code>Body</code>这个概念。下面我们会更加详细的阐述<code>Body</code>特性。</p><p>Soot使用<code>Body</code>存储方法的代码。在Soot中有四种类型的<code>Body</code>，每种<code>Body</code>都是一种中间表示(IR)</p><ul><li>BafBody</li><li>JimpleBody</li><li>ShimpleBody</li><li>GrimpBody</li></ul><p>在<code>Body</code>中三个最主要的链就是 </p><ul><li><code>Unit</code>链</li><li><code>Local</code>链</li><li><code>Trap</code>链。</li></ul><p>下面的例子将演示每个链的具体规则：</p><p>看一下下面的Java方法</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><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[] argv)</span> <span class="keyword">throws</span> Exception</span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">x</span> <span class="operator">=</span> <span class="number">2</span>, y = <span class="number">6</span>;</span><br><span class="line"></span><br><span class="line">    System.out.println(<span class="string">&quot;Hi!&quot;</span>);</span><br><span class="line">    System.out.println(x * y + y);</span><br><span class="line">    <span class="keyword">try</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">z</span> <span class="operator">=</span> y * x;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">catch</span> (Exception e)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">throw</span> e;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>经过<code>Jimplification</code>之后，我们得到了经过简化的<code>jimple</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="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(java.lang.String[])</span> <span class="keyword">throws</span> java.lang.Exception</span><br><span class="line">&#123;</span><br><span class="line">    java.lang.String[] r0;</span><br><span class="line">    <span class="type">int</span> i0, i1, i2, $i3, $i4;</span><br><span class="line">    java.io.PrintStream $r1, $r2;</span><br><span class="line">    java.lang.Exception $r3, r4;</span><br><span class="line"></span><br><span class="line">    r0 := <span class="meta">@parameter0</span>;</span><br><span class="line">    i0 = <span class="number">2</span>;</span><br><span class="line">    i1 = <span class="number">6</span>;</span><br><span class="line">    $r1 = java.lang.System.out;</span><br><span class="line">    $r1.println(``Hi!<span class="string">&#x27;&#x27;</span>);</span><br><span class="line">    $r2 = java.lang.System.out;</span><br><span class="line">    $i3 = i0 * i1;</span><br><span class="line">    $i4 = $i3 + i1;</span><br><span class="line">    $r2.println($i4);</span><br><span class="line"></span><br><span class="line"> label0:</span><br><span class="line">    i2 = i1 * i0;</span><br><span class="line"></span><br><span class="line"> label1:</span><br><span class="line">    <span class="keyword">goto</span> label3;</span><br><span class="line"></span><br><span class="line"> label2:</span><br><span class="line">    $r3 := <span class="meta">@caughtexception</span>;</span><br><span class="line">    r4 = $r3;</span><br><span class="line">    <span class="keyword">throw</span> r4;</span><br><span class="line"></span><br><span class="line"> label3:</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">catch</span> java.lang.Exception from label0 to label1 with label2;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="Local-variables"><a href="#Local-variables" class="headerlink" title="Local variables"></a>Local variables</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><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">java.lang.String[] r0;</span><br><span class="line"><span class="type">int</span> i0, i1, i2, $i3, $i4;</span><br><span class="line">java.io.PrintStream $r1, $r2;</span><br><span class="line">java.lang.Exception $r3, r4;</span><br></pre></td></tr></table></figure><p>本地变量存储在<code>localChain</code>里，可以通过<code>body.getLocals()</code>方法访问这些本地变量。每一种中间表示都可以用自己的方式实现本地变量。但是，不管如何实现，对于每一个变量<code>Local r0</code>都要能正确调用<code>r0.getName()</code>, <code>r0.getType()</code>, <code>r0.setName()</code> 和 <code>r0.setType()</code>。注意本地变量是必须包含类型的。</p><h3 id="Traps"><a href="#Traps" class="headerlink" title="Traps"></a>Traps</h3><p>为了支持Java的异常处理，Soot定义了traps概念。在Java的字节码中处理异常是通过一个元组(tuple)表示的(<code>exception</code>, <code>start</code>, <code>stop</code>, <code>handler</code>). 在start和stop之间(<code>[start, stop)</code>)的字节码中,如果有异常抛出来，后面的handler将处理该异常 </p><p>下面的trap就对应一个异常处理</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="keyword">catch</span> java.lang.Exception from label0 to label1 with label2;</span><br></pre></td></tr></table></figure><h3 id="Units"><a href="#Units" class="headerlink" title="Units"></a>Units</h3><p><code>Body</code>中最有趣的部分就是它的<code>Unit</code>链了。这是<code>Body</code>中真正的代码部分。<code>Jimple</code>提供了<code>Unit</code>的<code>Stmt</code>实现，而<code>Gimp</code>提供了<code>Unit</code>的<code>Inst</code>实现。这说明，每一个IR都有它自己独有的语句概念。</p><p>Jimple 一个简单的<code>Stmt</code>例子就是<code>AssignStmt</code>, 这是一个Jimple的赋值语句。例如</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">x = y + z;</span><br></pre></td></tr></table></figure><h3 id="Value"><a href="#Value" class="headerlink" title="Value"></a>Value</h3><p>代码总是基于数据运行。为了表达数据，Soot提供了<code>Value</code>接口。下面是一些不同的<code>Value</code>实现</p><ul><li><code>Local</code>s</li><li><code>Constant</code>s</li><li><code>Expressions</code> (<code>Expr</code>)</li><li><code>ParameterRefs</code>, <code>CaughtExceptionRefs</code> 和 <code>ThisRefs</code>.</li></ul><p><code>Expr</code> 接口有一整套的实现，例如 <code>NewExpr</code> 和 <code>AddExpr</code>。一般来说，<code>Expr</code> 接受一个或者多个<code>Value</code>然后产出一个<code>Value</code>。</p><p>下面我们看一个真实的<code>Value</code>示例</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">x = y + <span class="number">2</span>;</span><br></pre></td></tr></table></figure><p>这是一个 <code>AssignStmt</code>, 做操作符是<code>x</code>, 右操作符是<code>y + 2</code>(这是一个<code>AddExpr</code>). <code>AddExpr</code> 包含俩个操作数<code>y</code>和<code>2</code>, <code>y</code>是一个变量<code>Local</code>而<code>2</code>是一个常量<code>Constant</code>.</p><p>在Jimple中，我们强制要求，所有的<code>Value</code>最多只能包含一个表达式。Grimp取消了这个限制，从而可以产生更加易读但是更难分析的代码。</p><h3 id="Boxes"><a href="#Boxes" class="headerlink" title="Boxes"></a>Boxes</h3><p>Boxes 在Soot中是无处不在的。要时刻记住<code>Box 就是一个指针</code>。它提供了直接访问Soot对象的方式。</p><p>其实更能表达指针含义的名字是<code>Ref</code>而不是<code>Box</code>，可是<code>Ref</code>在Soot中已经被赋予了其他含义，因此我们只能用<code>Box</code>这个名字了。</p><p>在Soot中有俩种<code>Box</code></p><ul><li><code>ValueBox</code></li><li><code>UnitBox</code></li></ul><p>通过名字也能很清晰地看出，<code>ValueBox</code> 指向的是<code>Value</code>而<code>UnitBox</code>指向的是<code>Unit</code>，这很类似于c++中的<code>Unit *</code>和<code>Value *</code></p><h3 id="UnitBox"><a href="#UnitBox" class="headerlink" title="UnitBox"></a>UnitBox</h3><p>某些<code>Unit</code>需要包含一些指向其他Unit的引用。例如<code>GotoStmt</code>需要知道它的目标是什么。因此Soot提供了<code>UnitBox</code>。</p><p>例如下面的jimple代码</p><figure class="highlight java"><table><tr><td class="gutter"><pre><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">    x = <span class="number">5</span>;</span><br><span class="line">    <span class="keyword">goto</span> l2;</span><br><span class="line">    y = <span class="number">3</span>;</span><br><span class="line">l2: z = <span class="number">9</span>;</span><br></pre></td></tr></table></figure><p>每一个<code>Unit</code>都必须提供<code>getUnitBoxes()</code>方法。对于大多数<code>UnitBox</code>返回的都是一个空列表。但是在<code>GotoStmt</code>中，<code>getUnitBoxes()</code>返回了一个单元素列表，它返回的是一个指向了<code>l2</code>的<code>Box</code>。</p><p>注意，<code>SwitchStmt</code> 通常会返回一个多元素的<code>Box</code>列表。</p><p><code>Box</code>在修改代码时非常有用。例如我们有如下一个语句</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">s: <span class="keyword">goto</span> l2;</span><br></pre></td></tr></table></figure><p>还有一个l2语句</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">l2:  <span class="keyword">goto</span> l3;</span><br></pre></td></tr></table></figure><p>我们可以很清楚的看到，不管s是什么类型，s都会指向l3，而不是l2. 对于这种情况, 我们可以将所有的<code>Unit</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="keyword">public</span> <span class="keyword">void</span> <span class="title function_">readjustJumps</span><span class="params">(Unit s, Unit oldU, Unit newU)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">Iterator</span> <span class="variable">ubIt</span> <span class="operator">=</span> s.getUnitBoxes.iterator();</span><br><span class="line">    <span class="keyword">while</span> (ubIt.hasNext())</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="type">StmtBox</span> <span class="variable">tb</span> <span class="operator">=</span> (StmtBox)ubIt.next();</span><br><span class="line">        <span class="type">Stmt</span> <span class="variable">targ</span> <span class="operator">=</span> (Stmt)tb.getUnit();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (targ == oldU)</span><br><span class="line">            tb.setUnit(newU);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Some code similar to this is used in Unit itself, to enable the creation of PatchingChain, an implementation of Chain which adjusts pointers to Units which get removed from the Chain.</p><h3 id="ValueBox"><a href="#ValueBox" class="headerlink" title="ValueBox"></a>ValueBox</h3><p>与<code>Unit</code>情况类似，我们需要一种指向<code>Value</code>的指针，<code>ValueBox</code>. </p><p>我们可以用<code>ValueBox</code>做常量展开，例如<code>AssignStmt</code>会运算一个<code>AddExpr</code>加法表达式将俩个常量加到一起，我们可以静态地将它们加到一起，然后再将结果放到<code>UseBox</code>.</p><p>下面是<code>AddExpr</code>的一种展开</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">foldAdds</span><span class="params">(Unit u)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">Iterator</span> <span class="variable">ubIt</span> <span class="operator">=</span> u.getUseBoxes().iterator();</span><br><span class="line">    <span class="keyword">while</span> (ubIt.hasNext())</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="type">ValueBox</span> <span class="variable">vb</span> <span class="operator">=</span> (ValueBox) ubIt.next();</span><br><span class="line">        <span class="type">Value</span> <span class="variable">v</span> <span class="operator">=</span> vb.getValue();</span><br><span class="line">        <span class="keyword">if</span> (v <span class="keyword">instanceof</span> AddExpr)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="type">AddExpr</span> <span class="variable">ae</span> <span class="operator">=</span> (AddExpr) v;</span><br><span class="line">            <span class="type">Value</span> <span class="variable">lo</span> <span class="operator">=</span> ae.getOp1(), ro = ae.getOp2();</span><br><span class="line">            <span class="keyword">if</span> (lo <span class="keyword">instanceof</span> IntConstant &amp;&amp; ro <span class="keyword">instanceof</span> IntConstant)</span><br><span class="line">            &#123;</span><br><span class="line">                <span class="type">IntConstant</span> <span class="variable">l</span> <span class="operator">=</span> (IntConstant) lo,</span><br><span class="line">                      r = (IntConstant) ro;</span><br><span class="line">                <span class="type">int</span> <span class="variable">sum</span> <span class="operator">=</span> l.value + r.value;</span><br><span class="line">                vb.setValue(IntConstant.v(sum));</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>注意，这段代码可以忽略类型，基于任何Unit都可以正常工作。</p><h3 id="Unit-revisited"><a href="#Unit-revisited" class="headerlink" title="Unit revisited"></a>Unit revisited</h3><p>现在我们看看<code>Unit</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="keyword">public</span> List <span class="title function_">getUseBoxes</span><span class="params">()</span>;</span><br><span class="line"><span class="keyword">public</span> List <span class="title function_">getDefBoxes</span><span class="params">()</span>;</span><br><span class="line"><span class="keyword">public</span> List <span class="title function_">getUseAndDefBoxes</span><span class="params">()</span>;</span><br></pre></td></tr></table></figure><p>上面这些方法会返回当前<code>Unit</code>使用到或者定义的<code>ValueBox</code>列表。<code>getUseBoxes()</code>会返回所有使用到的value，其中包含了构成它们的表达式。</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="keyword">public</span> List <span class="title function_">getUnitBoxes</span><span class="params">()</span>;</span><br></pre></td></tr></table></figure><p>这个方法返回<code>UnitBox</code>列表, 指向当前方法的所有的unit(方法调用图？).</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="keyword">public</span> List <span class="title function_">getBoxesPointingToThis</span><span class="params">()</span>;</span><br></pre></td></tr></table></figure><p>这个方法返回<code>UnitBox</code>列表, 包含了所有目标是当前unit的Unit列表。</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="type">boolean</span> <span class="title function_">fallsThrough</span><span class="params">()</span>;</span><br><span class="line"><span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">branches</span><span class="params">()</span>;</span><br></pre></td></tr></table></figure><p>These methods have to do with the flow of execution after this Unit. The former method returns true if execution can continue to the following Unit, while the latter returns true if execution might continue to some Unit which isn’t immediately after this one.</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="keyword">public</span> <span class="keyword">void</span> <span class="title function_">redirectJumpsToThisTo</span><span class="params">(Unit newLocation)</span>;</span><br></pre></td></tr></table></figure><p>This method uses getBoxesPointingToThis to change all jumps to this Unit, pointing them instead at newLocation.</p>]]></content>
    
    
      
      
    <summary type="html">&lt;script src=&quot;/assets/asciinema-player.js&quot;&gt;&lt;/script&gt;&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;/assets/asciinema-player.css&quot; /&gt;
&lt;h2 id=&quot;Fun</summary>
      
    
    
    
    <category term="翻译" scheme="https://wangmingco.github.io/categories/%E7%BF%BB%E8%AF%91/"/>
    
    
    <category term="soot" scheme="https://wangmingco.github.io/tags/soot/"/>
    
  </entry>
  
  <entry>
    <title>JNI 返回 Java对象</title>
    <link href="https://wangmingco.github.io/2022/07/14/Java/jvm/JNI%20%E8%BF%94%E5%9B%9E%20Java%E5%AF%B9%E8%B1%A1/"/>
    <id>https://wangmingco.github.io/2022/07/14/Java/jvm/JNI%20%E8%BF%94%E5%9B%9E%20Java%E5%AF%B9%E8%B1%A1/</id>
    <published>2022-07-14T00:00:00.000Z</published>
    <updated>2025-12-19T01:50:58.662Z</updated>
    
    <content type="html"><![CDATA[<script src="/assets/asciinema-player.js"></script><link rel="stylesheet" type="text/css" href="/assets/asciinema-player.css" /><p>Java代码</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> jnijava;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">JniJava</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><br><span class="line">        System.load(<span class="string">&quot;/Users/wangming/NetBeansProjects/jnicpp/dist/Debug/GNU-MacOSX/libjnicpp.dylib&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;JniJava&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="type">Obj</span> <span class="variable">obj</span> <span class="operator">=</span> print();</span><br><span class="line">        System.out.println(obj.str);</span><br><span class="line">        System.out.println(obj.i);</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">native</span> Obj <span class="title function_">print</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Obj</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> String str;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> i;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>jni代码, 这是c语言的写法。如果要换成c++的需要将<code>(*env)-&gt;FindClass(env, &quot;jnijava/Obj&quot;);</code>转换成<code>env-&gt;FindClass(&quot;jnijava/Obj&quot;)</code></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;JniJava.h&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function">jobject JNICALL <span class="title">Java_jnijava_JniJava_print</span></span></span><br><span class="line"><span class="function">  <span class="params">(JNIEnv *env, jclass object)</span> </span>&#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 找到目标类</span></span><br><span class="line">    jclass objClass = (*env)-&gt;<span class="built_in">FindClass</span>(env, <span class="string">&quot;jnijava/Obj&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 调用类的实例化方法，然后实例化</span></span><br><span class="line">    jmethodID initMethod = (*env)-&gt;<span class="built_in">GetMethodID</span>(env, objClass, <span class="string">&quot;&lt;init&gt;&quot;</span>, <span class="string">&quot;()V&quot;</span>);</span><br><span class="line">    jobject obj = (*env)-&gt;<span class="built_in">NewObject</span>(env, objClass, initMethod);</span><br><span class="line">        </span><br><span class="line">    <span class="comment">// 找到要赋值的属性</span></span><br><span class="line">    jfieldID str = (*env)-&gt;<span class="built_in">GetFieldID</span>(env, objClass, <span class="string">&quot;str&quot;</span>, <span class="string">&quot;Ljava/lang/String;&quot;</span>);</span><br><span class="line">    jfieldID i = (*env)-&gt;<span class="built_in">GetFieldID</span>(env, objClass, <span class="string">&quot;i&quot;</span>, <span class="string">&quot;I&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 对属性进行赋值</span></span><br><span class="line">    (*env)-&gt;<span class="built_in">SetObjectField</span>(env, obj, str, (*env)-&gt;<span class="built_in">NewStringUTF</span>(env, <span class="string">&quot;jnicpp&quot;</span>));</span><br><span class="line">    (*env)-&gt;<span class="built_in">SetIntField</span>(env, obj, i, <span class="number">9999</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> obj;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这就直接在jni里返回了一个Java对象，那么这个Java对象是如何与GC关联起来的呢？关键就在于<code>(*env)-&gt;NewObject(env, objClass, initMethod);</code></p><p>在jdk17中<code>jdk-jdk-17-35/src/hotspot/share/prims/jni.cpp</code> 该方法时如下定义的</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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="built_in">JNI_ENTRY</span>(jobject, <span class="built_in">jni_NewObject</span>(JNIEnv *env, jclass clazz, jmethodID methodID, ...))</span><br><span class="line">  <span class="built_in">HOTSPOT_JNI_NEWOBJECT_ENTRY</span>(env, clazz, (<span class="type">uintptr_t</span>) methodID);</span><br><span class="line"></span><br><span class="line">  jobject obj = <span class="literal">NULL</span>;</span><br><span class="line">  <span class="built_in">DT_RETURN_MARK</span>(NewObject, jobject, (<span class="type">const</span> jobject&amp;)obj);</span><br><span class="line"></span><br><span class="line">  instanceOop i = InstanceKlass::<span class="built_in">allocate_instance</span>(JNIHandles::<span class="built_in">resolve_non_null</span>(clazz), CHECK_NULL);</span><br><span class="line">  obj = JNIHandles::<span class="built_in">make_local</span>(THREAD, i);</span><br><span class="line">  va_list args;</span><br><span class="line">  <span class="built_in">va_start</span>(args, methodID);</span><br><span class="line">  <span class="function">JavaValue <span class="title">jvalue</span><span class="params">(T_VOID)</span></span>;</span><br><span class="line">  <span class="function">JNI_ArgumentPusherVaArg <span class="title">ap</span><span class="params">(methodID, args)</span></span>;</span><br><span class="line">  <span class="built_in">jni_invoke_nonstatic</span>(env, &amp;jvalue, obj, JNI_NONVIRTUAL, methodID, &amp;ap, CHECK_NULL);</span><br><span class="line">  <span class="built_in">va_end</span>(args);</span><br><span class="line">  <span class="keyword">return</span> obj;</span><br><span class="line">JNI_END</span><br></pre></td></tr></table></figure><p>这里分配一个对象出来 <code>InstanceKlass::allocate_instance(JNIHandles::resolve_non_null(clazz), CHECK_NULL);</code></p><p><code>jdk-jdk-17-35/src/hotspot/share/oops/instanceKlass.inline.hpp</code></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">inline</span> instanceOop <span class="title">InstanceKlass::allocate_instance</span><span class="params">(oop java_class, TRAPS)</span> </span>&#123;</span><br><span class="line">  Klass* k = java_lang_Class::<span class="built_in">as_Klass</span>(java_class);</span><br><span class="line">  <span class="keyword">if</span> (k == <span class="literal">NULL</span>) &#123;</span><br><span class="line">    <span class="function">ResourceMark <span class="title">rm</span><span class="params">(THREAD)</span></span>;</span><br><span class="line">    <span class="built_in">THROW_</span>(vmSymbols::<span class="built_in">java_lang_InstantiationException</span>(), <span class="literal">NULL</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  InstanceKlass* ik = <span class="built_in">cast</span>(k);</span><br><span class="line">  ik-&gt;<span class="built_in">check_valid_for_instantiation</span>(<span class="literal">false</span>, CHECK_NULL);</span><br><span class="line">  ik-&gt;<span class="built_in">initialize</span>(CHECK_NULL);</span><br><span class="line">  <span class="keyword">return</span> ik-&gt;<span class="built_in">allocate_instance</span>(THREAD);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>最后一步直接调用<code>InstanceKlass</code>的<code>allocate_instance(THREAD)</code>方法</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">instanceOop <span class="title">InstanceKlass::allocate_instance</span><span class="params">(TRAPS)</span> </span>&#123;</span><br><span class="line">  <span class="type">bool</span> has_finalizer_flag = <span class="built_in">has_finalizer</span>(); <span class="comment">// Query before possible GC</span></span><br><span class="line">  <span class="type">int</span> size = <span class="built_in">size_helper</span>();  <span class="comment">// Query before forming handle.</span></span><br><span class="line"></span><br><span class="line">  instanceOop i;</span><br><span class="line"></span><br><span class="line">  i = (instanceOop)Universe::<span class="built_in">heap</span>()-&gt;<span class="built_in">obj_allocate</span>(<span class="keyword">this</span>, size, CHECK_NULL);</span><br><span class="line">  <span class="keyword">if</span> (has_finalizer_flag &amp;&amp; !RegisterFinalizersAtInit) &#123;</span><br><span class="line">    i = <span class="built_in">register_finalizer</span>(i, CHECK_NULL);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> i;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>最终也就是调用<code>jdk-jdk-17-35/src/hotspot/share/gc/shared/collectedHeap.inline.hpp</code>的</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">inline</span> oop <span class="title">CollectedHeap::obj_allocate</span><span class="params">(Klass* klass, <span class="type">int</span> size, TRAPS)</span> </span>&#123;</span><br><span class="line">  <span class="function">ObjAllocator <span class="title">allocator</span><span class="params">(klass, size, THREAD)</span></span>;</span><br><span class="line">  <span class="keyword">return</span> allocator.<span class="built_in">allocate</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>继续往里面看<code>jdk-jdk-17-35/src/hotspot/share/gc/shared/memAllocator.cpp</code></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">oop <span class="title">MemAllocator::allocate</span><span class="params">()</span> <span class="type">const</span> </span>&#123;</span><br><span class="line">  oop obj = <span class="literal">NULL</span>;</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="function">Allocation <span class="title">allocation</span><span class="params">(*<span class="keyword">this</span>, &amp;obj)</span></span>;</span><br><span class="line">    HeapWord* mem = <span class="built_in">mem_allocate</span>(allocation);</span><br><span class="line">    <span class="keyword">if</span> (mem != <span class="literal">NULL</span>) &#123;</span><br><span class="line">      obj = <span class="built_in">initialize</span>(mem);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="comment">// The unhandled oop detector will poison local variable obj,</span></span><br><span class="line">      <span class="comment">// so reset it to NULL if mem is NULL.</span></span><br><span class="line">      obj = <span class="literal">NULL</span>;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> obj;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>继续看<code>mem_allocate(allocation);</code></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">HeapWord* <span class="title">MemAllocator::mem_allocate</span><span class="params">(Allocation&amp; allocation)</span> <span class="type">const</span> </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (UseTLAB) &#123;</span><br><span class="line">    HeapWord* result = <span class="built_in">allocate_inside_tlab</span>(allocation);</span><br><span class="line">    <span class="keyword">if</span> (result != <span class="literal">NULL</span>) &#123;</span><br><span class="line">      <span class="keyword">return</span> result;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">allocate_outside_tlab</span>(allocation);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里是判断一下，是否在TLAB上面分配，我们关心的是heap的GC，直接看<code>allocate_outside_tlab(allocation);</code></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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="function">HeapWord* <span class="title">MemAllocator::allocate_outside_tlab</span><span class="params">(Allocation&amp; allocation)</span> <span class="type">const</span> </span>&#123;</span><br><span class="line">  allocation._allocated_outside_tlab = <span class="literal">true</span>;</span><br><span class="line">  HeapWord* mem = Universe::<span class="built_in">heap</span>()-&gt;<span class="built_in">mem_allocate</span>(_word_size, &amp;allocation._overhead_limit_exceeded);</span><br><span class="line">  <span class="keyword">if</span> (mem == <span class="literal">NULL</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> mem;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="built_in">NOT_PRODUCT</span>(Universe::<span class="built_in">heap</span>()-&gt;<span class="built_in">check_for_non_bad_heap_word_value</span>(mem, _word_size));</span><br><span class="line">  <span class="type">size_t</span> size_in_bytes = _word_size * HeapWordSize;</span><br><span class="line">  _thread-&gt;<span class="built_in">incr_allocated_bytes</span>(size_in_bytes);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> mem;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>heap()-&gt;mem_allocate(_word_size, &amp;allocation._overhead_limit_exceeded)</code> 这一个就是个多态方法了，取决于 <code>heap()</code> 方法返回的是<code>CollectedHeap</code>何种子类了</p><ul><li><code>ParallelScavengeHeap</code> : <code>jdk-jdk-17-35/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp</code></li><li><code>GenCollectedHeap</code> : <code>jdk-jdk-17-35/src/hotspot/share/gc/shared/genCollectedHeap.hpp</code></li><li><code>EpsilonHeap</code> : <code>jdk-jdk-17-35/src/hotspot/share/gc/epsilon/epsilonHeap.hpp</code></li><li><code>G1CollectedHeap</code> : <code>jdk-jdk-17-35/src/hotspot/share/gc/g1/g1CollectedHeap.hpp</code></li><li><code>ShenandoahHeap</code> : <code>jdk-jdk-17-35/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp</code></li><li><code>ZCollectedHeap</code> : <code>jdk-jdk-17-35/src/hotspot/share/gc/z/zCollectedHeap.hpp</code></li></ul><p>我们看一下<code>GenCollectedHeap</code> (分成年轻代和老年代俩种区域的分层回收heap)</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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">HeapWord* <span class="title">GenCollectedHeap::mem_allocate</span><span class="params">(<span class="type">size_t</span> size,</span></span></span><br><span class="line"><span class="params"><span class="function">                                         <span class="type">bool</span>* gc_overhead_limit_was_exceeded)</span> </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">mem_allocate_work</span>(size,</span><br><span class="line">                           <span class="literal">false</span> <span class="comment">/* is_tlab */</span>,</span><br><span class="line">                           gc_overhead_limit_was_exceeded);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>下面就直接调用GC算法分配</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="function">HeapWord* <span class="title">GenCollectedHeap::mem_allocate_work</span><span class="params">(<span class="type">size_t</span> size,</span></span></span><br><span class="line"><span class="params"><span class="function">                                              <span class="type">bool</span> is_tlab,</span></span></span><br><span class="line"><span class="params"><span class="function">                                              <span class="type">bool</span>* gc_overhead_limit_was_exceeded)</span> </span>&#123;</span><br><span class="line">  <span class="comment">// In general gc_overhead_limit_was_exceeded should be false so</span></span><br><span class="line">  <span class="comment">// set it so here and reset it to true only if the gc time</span></span><br><span class="line">  <span class="comment">// limit is being exceeded as checked below.</span></span><br><span class="line">  *gc_overhead_limit_was_exceeded = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line">  HeapWord* result = <span class="literal">NULL</span>;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// Loop until the allocation is satisfied, or unsatisfied after GC.</span></span><br><span class="line">  <span class="keyword">for</span> (uint try_count = <span class="number">1</span>, gclocker_stalled_count = <span class="number">0</span>; <span class="comment">/* return or throw */</span>; try_count += <span class="number">1</span>) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// First allocation attempt is lock-free.</span></span><br><span class="line">    Generation *young = _young_gen;</span><br><span class="line">    <span class="built_in">assert</span>(young-&gt;<span class="built_in">supports_inline_contig_alloc</span>(),</span><br><span class="line">      <span class="string">&quot;Otherwise, must do alloc within heap lock&quot;</span>);</span><br><span class="line">    <span class="keyword">if</span> (young-&gt;<span class="built_in">should_allocate</span>(size, is_tlab)) &#123;</span><br><span class="line">      result = young-&gt;<span class="built_in">par_allocate</span>(size, is_tlab);</span><br><span class="line">      <span class="keyword">if</span> (result != <span class="literal">NULL</span>) &#123;</span><br><span class="line">        <span class="built_in">assert</span>(<span class="built_in">is_in_reserved</span>(result), <span class="string">&quot;result not in heap&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    uint gc_count_before;  <span class="comment">// Read inside the Heap_lock locked region.</span></span><br><span class="line">    &#123;</span><br><span class="line">      <span class="function">MutexLocker <span class="title">ml</span><span class="params">(Heap_lock)</span></span>;</span><br><span class="line">      <span class="built_in">log_trace</span>(gc, alloc)(<span class="string">&quot;GenCollectedHeap::mem_allocate_work: attempting locked slow path allocation&quot;</span>);</span><br><span class="line">      <span class="comment">// Note that only large objects get a shot at being</span></span><br><span class="line">      <span class="comment">// allocated in later generations.</span></span><br><span class="line">      <span class="type">bool</span> first_only = !<span class="built_in">should_try_older_generation_allocation</span>(size);</span><br><span class="line"></span><br><span class="line">      result = <span class="built_in">attempt_allocation</span>(size, is_tlab, first_only);</span><br><span class="line">      <span class="keyword">if</span> (result != <span class="literal">NULL</span>) &#123;</span><br><span class="line">        <span class="built_in">assert</span>(<span class="built_in">is_in_reserved</span>(result), <span class="string">&quot;result not in heap&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="keyword">if</span> (GCLocker::<span class="built_in">is_active_and_needs_gc</span>()) &#123;</span><br><span class="line">        <span class="keyword">if</span> (is_tlab) &#123;</span><br><span class="line">          <span class="keyword">return</span> <span class="literal">NULL</span>;  <span class="comment">// Caller will retry allocating individual object.</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (!<span class="built_in">is_maximal_no_gc</span>()) &#123;</span><br><span class="line">          <span class="comment">// Try and expand heap to satisfy request.</span></span><br><span class="line">          result = <span class="built_in">expand_heap_and_allocate</span>(size, is_tlab);</span><br><span class="line">          <span class="comment">// Result could be null if we are out of space.</span></span><br><span class="line">          <span class="keyword">if</span> (result != <span class="literal">NULL</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> result;</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">if</span> (gclocker_stalled_count &gt; GCLockerRetryAllocationCount) &#123;</span><br><span class="line">          <span class="keyword">return</span> <span class="literal">NULL</span>; <span class="comment">// We didn&#x27;t get to do a GC and we didn&#x27;t get any memory.</span></span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// If this thread is not in a jni critical section, we stall</span></span><br><span class="line">        <span class="comment">// the requestor until the critical section has cleared and</span></span><br><span class="line">        <span class="comment">// GC allowed. When the critical section clears, a GC is</span></span><br><span class="line">        <span class="comment">// initiated by the last thread exiting the critical section; so</span></span><br><span class="line">        <span class="comment">// we retry the allocation sequence from the beginning of the loop,</span></span><br><span class="line">        <span class="comment">// rather than causing more, now probably unnecessary, GC attempts.</span></span><br><span class="line">        JavaThread* jthr = JavaThread::<span class="built_in">current</span>();</span><br><span class="line">        <span class="keyword">if</span> (!jthr-&gt;<span class="built_in">in_critical</span>()) &#123;</span><br><span class="line">          <span class="function">MutexUnlocker <span class="title">mul</span><span class="params">(Heap_lock)</span></span>;</span><br><span class="line">          <span class="comment">// Wait for JNI critical section to be exited</span></span><br><span class="line">          GCLocker::<span class="built_in">stall_until_clear</span>();</span><br><span class="line">          gclocker_stalled_count += <span class="number">1</span>;</span><br><span class="line">          <span class="keyword">continue</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">          <span class="keyword">if</span> (CheckJNICalls) &#123;</span><br><span class="line">            <span class="built_in">fatal</span>(<span class="string">&quot;Possible deadlock due to allocating while&quot;</span></span><br><span class="line">                  <span class="string">&quot; in jni critical section&quot;</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><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="comment">// Read the gc count while the heap lock is held.</span></span><br><span class="line">      gc_count_before = <span class="built_in">total_collections</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function">VM_GenCollectForAllocation <span class="title">op</span><span class="params">(size, is_tlab, gc_count_before)</span></span>;</span><br><span class="line">    VMThread::<span class="built_in">execute</span>(&amp;op);</span><br><span class="line">    <span class="keyword">if</span> (op.<span class="built_in">prologue_succeeded</span>()) &#123;</span><br><span class="line">      result = op.<span class="built_in">result</span>();</span><br><span class="line">      <span class="keyword">if</span> (op.<span class="built_in">gc_locked</span>()) &#123;</span><br><span class="line">         <span class="built_in">assert</span>(result == <span class="literal">NULL</span>, <span class="string">&quot;must be NULL if gc_locked() is true&quot;</span>);</span><br><span class="line">         <span class="keyword">continue</span>;  <span class="comment">// Retry and/or stall as necessary.</span></span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="comment">// Allocation has failed and a collection</span></span><br><span class="line">      <span class="comment">// has been done.  If the gc time limit was exceeded the</span></span><br><span class="line">      <span class="comment">// this time, return NULL so that an out-of-memory</span></span><br><span class="line">      <span class="comment">// will be thrown.  Clear gc_overhead_limit_exceeded</span></span><br><span class="line">      <span class="comment">// so that the overhead exceeded does not persist.</span></span><br><span class="line"></span><br><span class="line">      <span class="type">const</span> <span class="type">bool</span> limit_exceeded = <span class="built_in">size_policy</span>()-&gt;<span class="built_in">gc_overhead_limit_exceeded</span>();</span><br><span class="line">      <span class="type">const</span> <span class="type">bool</span> softrefs_clear = <span class="built_in">soft_ref_policy</span>()-&gt;<span class="built_in">all_soft_refs_clear</span>();</span><br><span class="line"></span><br><span class="line">      <span class="keyword">if</span> (limit_exceeded &amp;&amp; softrefs_clear) &#123;</span><br><span class="line">        *gc_overhead_limit_was_exceeded = <span class="literal">true</span>;</span><br><span class="line">        <span class="built_in">size_policy</span>()-&gt;<span class="built_in">set_gc_overhead_limit_exceeded</span>(<span class="literal">false</span>);</span><br><span class="line">        <span class="keyword">if</span> (op.<span class="built_in">result</span>() != <span class="literal">NULL</span>) &#123;</span><br><span class="line">          CollectedHeap::<span class="built_in">fill_with_object</span>(op.<span class="built_in">result</span>(), size);</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><span class="line">      <span class="built_in">assert</span>(result == <span class="literal">NULL</span> || <span class="built_in">is_in_reserved</span>(result),</span><br><span class="line">             <span class="string">&quot;result not in heap&quot;</span>);</span><br><span class="line">      <span class="keyword">return</span> result;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Give a warning if we seem to be looping forever.</span></span><br><span class="line">    <span class="keyword">if</span> ((QueuedAllocationWarningCount &gt; <span class="number">0</span>) &amp;&amp;</span><br><span class="line">        (try_count % QueuedAllocationWarningCount == <span class="number">0</span>)) &#123;</span><br><span class="line">          <span class="built_in">log_warning</span>(gc, ergo)(<span class="string">&quot;GenCollectedHeap::mem_allocate_work retries %d times,&quot;</span></span><br><span class="line">                                <span class="string">&quot; size=&quot;</span> SIZE_FORMAT <span class="string">&quot; %s&quot;</span>, try_count, size, is_tlab ? <span class="string">&quot;(TLAB)&quot;</span> : <span class="string">&quot;&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;script src=&quot;/assets/asciinema-player.js&quot;&gt;&lt;/script&gt;&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;/assets/asciinema-player.css&quot; /&gt;
&lt;p&gt;Java代码&lt;/</summary>
      
    
    
    
    <category term="Java" scheme="https://wangmingco.github.io/categories/Java/"/>
    
    
    <category term="jvm" scheme="https://wangmingco.github.io/tags/jvm/"/>
    
  </entry>
  
  <entry>
    <title>NetBeans Debug Cpp</title>
    <link href="https://wangmingco.github.io/2022/07/10/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/cpp/NetBeans%20Debug%20Cpp/"/>
    <id>https://wangmingco.github.io/2022/07/10/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/cpp/NetBeans%20Debug%20Cpp/</id>
    <published>2022-07-10T00:00:00.000Z</published>
    <updated>2025-12-19T01:50:58.674Z</updated>
    
    <content type="html"><![CDATA[<script src="/assets/asciinema-player.js"></script><link rel="stylesheet" type="text/css" href="/assets/asciinema-player.css" /><h2 id="安装插件"><a href="#安装插件" class="headerlink" title="安装插件"></a>安装插件</h2><p>NetBeans 自带的 C/Cpp 插件叫做 CPPLite Kit，需要安装ccls。这个我不熟悉，改用以前的插件</p><p><img src="https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/images/NetBeans/cpp_env_1.png"></p><p><img src="https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/images/NetBeans/cpp_env_2.png"></p><blockquote><p>安装这个插件目前需要jdk8(主要是使用upack2000那个工具)</p></blockquote><blockquote><p>在Mac上NetBeans是使用gdb调试的，但是Mac上使用gdb需要对gdb签名。网络上一堆签名教程，但是对于10.14之后的系统，需要如下签名<br>创建文件 gdb-entitlement.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></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="meta">&lt;!DOCTYPE <span class="keyword">plist</span> <span class="keyword">PUBLIC</span> <span class="string">&quot;-//Apple//DTD PLIST 1.0//EN&quot;</span> <span class="string">&quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">plist</span> <span class="attr">version</span>=<span class="string">&quot;1.0&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dict</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">key</span>&gt;</span>com.apple.security.cs.debugger<span class="tag">&lt;/<span class="name">key</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">true</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dict</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">plist</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">pre</span>&gt;</span></span><br></pre></td></tr></table></figure><p>然后调用<code>codesign --entitlements gdb-entitlement.xml -fs gdb-cert $(which gdb)</code></p><p>参考<a href="https://segmentfault.com/q/1010000004136334/a-1020000019448851">解决GDB在Mac下不能调试的问题 - ikingye的回答</a></p></blockquote><p>接下来就可以重新打开基于Makefile文件的Cpp工程了。</p><p><img src="https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/images/NetBeans/cpp_imort_1.png"></p><p><img src="https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/images/NetBeans/cpp_imort_2.png"></p><h2 id="Debug-JNI"><a href="#Debug-JNI" class="headerlink" title="Debug JNI"></a>Debug JNI</h2><p>使用NetBeans进行jni Debug</p><h2 id="Debug-JVM"><a href="#Debug-JVM" class="headerlink" title="Debug JVM"></a>Debug JVM</h2>]]></content>
    
    
      
      
    <summary type="html">&lt;script src=&quot;/assets/asciinema-player.js&quot;&gt;&lt;/script&gt;&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;/assets/asciinema-player.css&quot; /&gt;
&lt;h2 id=&quot;安装插</summary>
      
    
    
    
    <category term="编程语言" scheme="https://wangmingco.github.io/categories/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/"/>
    
    
    <category term="c/cpp" scheme="https://wangmingco.github.io/tags/c-cpp/"/>
    
  </entry>
  
  <entry>
    <title>React 学习</title>
    <link href="https://wangmingco.github.io/2022/04/12/%E5%89%8D%E7%AB%AF/REACT%20%E8%AF%AD%E6%B3%95/"/>
    <id>https://wangmingco.github.io/2022/04/12/%E5%89%8D%E7%AB%AF/REACT%20%E8%AF%AD%E6%B3%95/</id>
    <published>2022-04-12T10:21:00.000Z</published>
    <updated>2025-12-19T01:50:58.670Z</updated>
    
    <content type="html"><![CDATA[<script src="/assets/asciinema-player.js"></script><link rel="stylesheet" type="text/css" href="/assets/asciinema-player.css" /><h2 id="React"><a href="#React" class="headerlink" title="React"></a>React</h2><ul><li>JSX 语法</li><li>组件<ul><li>函数组件<ul><li>没有类组件的生命周期</li><li>函数式组件没有 this</li><li>函数式组件形参是 props 和类组件的 this.props 相同</li><li>hooks 函数组件状态<ul><li><code>const [state, setState] = useState(false);</code> 缓存状态。 在函数中定义一个状态 on, 和一个改变 on 值的方法 setOn().</li><li><code>useEffect(fn, [state])</code> 监听状态，状态变化第一个参数会执行，类似于一个 callback 函数，但是没有返回结果。TODO useEffect 在受控组件中的用途？？？</li><li><code>let f = useCallback(fn, [state])</code> 缓存 fn 函数(fn 默认不执行)。不必每次 render 的时候都创建一遍函数。如果 state 没发生变化，则以后每次调用 f 的时候都不会重新执行 fn</li><li><code>let f = useMemo(fn, [state])</code> 直接执行 fn 函数，然后 useMemo 返回 fn 结果。如果 state 没有发生变化，则以后直接取上次的缓存结果</li><li><code>let ref = useRef()</code> 类似于类组件的 ref</li><li><code>useContext</code> 生产者消费者模型，用于在多个组件间共享数据(看到的例子只有一个生产者，支持多个生产者吗？？？)</li><li><code>useReducer</code> </li></ul></li></ul></li><li>类组件<ul><li>状态<ul><li>setState() 提交状态， 只有通过这个方式修改 state, react 才会重新渲染页面</li><li>this.state.xxx 获取状态</li><li>setState() 同步异步</li></ul></li><li>属性<ul><li>组件传参</li><li>属性获取 this.props.xxx</li><li>属性验证 组件名称.propTypes = {} (需要引入 prop-types)</li><li>默认属性 组件名称.defaultProps = {}</li><li>可以使用 ES6 属性展开 {…obj}</li><li>函数式组件属性支持(但是函数式组件 16.8 之前不支持 state)</li></ul></li><li>ref 获取组件</li></ul></li><li>函数组件<ul><li>箭头函数</li></ul></li><li>事件处理<ul><li>this 问题<ul><li>箭头函数 this 指向外部</li><li>普通函数 this 不好用</li><li>调用传参</li></ul></li></ul></li><li>受控<ul><li>非受控组件 包含 state 的组件，状态组件</li><li>受控组件 只包含 props，不包含 state 的组件，无状态组件 (<input value="123">此时就是受控组件，input 框里面的内容是不可修改的)</li><li>表单<ul><li><code>&lt;input defaultValue=&quot;123&quot;&gt;</code> , 这里如果用 defaultValue 替换 value，将受控组件替换为非受控组件</li><li><code>&lt;input value=&#123;this.state.name&#125; onChange= (evt)=&gt; &#123;setState(&#123;name: evt.target.value&#125;)&#125; &gt;</code> 通过将状态与 value 绑定,</li></ul></li></ul></li><li>生命周期</li></ul></li><li>列表渲染<ul><li>key 值</li></ul></li><li>条件渲染<ul><li>三元操作符，&amp;&amp;操作符</li></ul></li><li>dangerouslySetInnerHTML<ul><li>富文本编辑</li><li>安全性，如何被攻击</li></ul></li></ul><h2 id="ES-6"><a href="#ES-6" class="headerlink" title="ES 6"></a>ES 6</h2><p>对象合并</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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">obj1 = &#123;</span><br><span class="line">  a : <span class="string">&quot;123&quot;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">obj2 = &#123;</span><br><span class="line">  b : <span class="string">&quot;456&quot;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">obj3 = &#123;...obj1, ...obj2&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 此时obj3就有了obj1和obj2的内容</span></span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;script src=&quot;/assets/asciinema-player.js&quot;&gt;&lt;/script&gt;&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;/assets/asciinema-player.css&quot; /&gt;
&lt;h2 id=&quot;Rea</summary>
      
    
    
    
    <category term="前端" scheme="https://wangmingco.github.io/categories/%E5%89%8D%E7%AB%AF/"/>
    
    
    <category term="React" scheme="https://wangmingco.github.io/tags/React/"/>
    
  </entry>
  
  <entry>
    <title>Centos7 编译调试 OpenJDK11</title>
    <link href="https://wangmingco.github.io/2022/01/10/Java/jvm/centos%E7%BC%96%E8%AF%91openjdk/"/>
    <id>https://wangmingco.github.io/2022/01/10/Java/jvm/centos%E7%BC%96%E8%AF%91openjdk/</id>
    <published>2022-01-10T00:00:00.000Z</published>
    <updated>2025-12-19T01:50:58.666Z</updated>
    
    <content type="html"><![CDATA[<script src="/assets/asciinema-player.js"></script><link rel="stylesheet" type="text/css" href="/assets/asciinema-player.css" /><h2 id="编译"><a href="#编译" class="headerlink" title="编译"></a>编译</h2><p>按照如下过程进行编译(测试机是一台2核4G内存的云虚拟机，编译过程较慢且偶尔会失败，最好是使用一个多核机器进行编译)</p><blockquote><p>本次构建是JDK11当前最新版<a href="https://github.com/openjdk/jdk11u/archive/refs/tags/jdk-11.0.14+8.zip">jdk11u-jdk-11.0.14-8</a>, 构建过程出现问题，参考构建教程 <code>jdk11u-jdk-11.0.14-8/doc/building.html</code>.</p></blockquote><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></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看系统版本</span></span><br><span class="line">cat /proc/version</span><br><span class="line">rpm -q centos-release</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装依赖环境</span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash"><span class="comment"># 安装 FreeType</span></span></span><br><span class="line">yum install -y freetype-devel </span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash"><span class="comment"># 安装 CUPS</span></span></span><br><span class="line">yum install -y cups-devel </span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash"><span class="comment"># 安装 ALSA</span></span></span><br><span class="line">yum install -y alsa-lib-devel</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash"><span class="comment"># 安装 libffi</span></span></span><br><span class="line">yum install -y libffi-devel</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash"><span class="comment"># 安装 Autoconf</span></span></span><br><span class="line">yum install -y autoconf</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash"><span class="comment"># 安装 X11</span></span></span><br><span class="line">yum install -y libxext-dev libx11-dev libXtst-devel libXt-devel libXrender-devel libXi-devel libXrandr-devel</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash"><span class="comment"># 安装 fontconfig</span></span></span><br><span class="line">yum install -y fontconfig</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装Boot-JDK, 编译openjdk-11，需要以jdk-11为bootjdk</span></span><br><span class="line">yum install -y java-11-openjdk-devel</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装开发工具</span></span><br><span class="line">yum groupinstall -y &quot;Development Tools&quot;</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">开始编译</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">sh ./configure --enable-debug</span></span><br><span class="line">sh ./configure --with-debug-level=slowdebug --with-native-debug-symbols=external</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">make all</span></span><br><span class="line">make images</span><br></pre></td></tr></table></figure><blockquote><p>本次编译使用centos7自带的gcc4.8版本，如果使用高版本，大于文档说的7.4版本，可能会出现编译错误, 那么在运行<code>./configure</code>时打开<code>--disable-warnings-as-errors</code> 配置,即可</p></blockquote><blockquote><p>jdk11u-jdk-11.0.14-8 版本JDK默认是ascii编码，如果想要使用UTF8编码的话，修改 <code>jdk11u-jdk-11.0.14-8/make/common/SetupJavaCompilers.gmk</code> 文件，将<code>-encoding ascii</code> 修改成 <code>-encoding utf-8</code></p></blockquote><p>整个安装编译环境以及编译过程如下:</p><div id="https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/videos/centos7_compile_openjdk11.cast"></div><script>AsciinemaPlayer.create("https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/videos/centos7_compile_openjdk11.cast",document.getElementById("https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/videos/centos7_compile_openjdk11.cast"));</script><p>升级GCC11(下面的方式只针对当前session有效)</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">sudo yum install centos-release-scl</span><br><span class="line">sudo yum install devtoolset-11-gcc*</span><br><span class="line">scl enable devtoolset-11 bash</span><br></pre></td></tr></table></figure><h2 id="debug"><a href="#debug" class="headerlink" title="debug"></a>debug</h2><p>在vscode中进行远程debug, 在vscode中安装如下插件</p><ul><li>vscode c/c++ 开发支持 <a href="https://github.com/microsoft/vscode-cpptools">C/C++ for Visual Studio Code</a></li><li>Remote-SSH 远程支持 <a href="">Visual Studio Code Remote - SSH</a></li></ul><div id="dplayer14" class="dplayer hexo-tag-dplayer-mark" style="margin-bottom: 20px;"></div><script>(function(){var player = new DPlayer({"container":document.getElementById("dplayer14"),"theme":"#FADFA3","video":{"url":"https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/videos/centos7_debug_openjdk11.mp4"},"danmaku":{"api":"https://api.prprpr.me/dplayer/"}});window.dplayers||(window.dplayers=[]);window.dplayers.push(player);})()</script> <p>在vscode里可以很方便的修改源码，然后重新编译，debug，yyds</p>]]></content>
    
    
      
      
    <summary type="html">&lt;script src=&quot;/assets/asciinema-player.js&quot;&gt;&lt;/script&gt;&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;/assets/asciinema-player.css&quot; /&gt;
&lt;h2 id=&quot;编译&quot;</summary>
      
    
    
    
    <category term="Java" scheme="https://wangmingco.github.io/categories/Java/"/>
    
    
    <category term="jvm" scheme="https://wangmingco.github.io/tags/jvm/"/>
    
  </entry>
  
  <entry>
    <title>地图景点</title>
    <link href="https://wangmingco.github.io/2021/12/09/html/travel/"/>
    <id>https://wangmingco.github.io/2021/12/09/html/travel/</id>
    <published>2021-12-09T15:40:00.000Z</published>
    <updated>2025-12-19T01:50:58.670Z</updated>
    
    <content type="html"><![CDATA[<script src="/assets/asciinema-player.js"></script><link rel="stylesheet" type="text/css" href="/assets/asciinema-player.css" /><p><a href="/bjb.html">北京景点地图-百度</a><br><a href="/bjg.html">北京景点地图-高德</a></p><p><a href="/html/qinhuangdao_baidu.html">秦皇岛景点地图</a></p><!-- <p>    <div style="width:820px; height:1000px;border:none;text-align:center">        <iframe             allowtransparency="yes"             frameborder="0"             width="820px"             height="1000px"             scrolling="auto"             style="box-shadow: 0px 0px 20px -10px #888;"             src="/html/bj_baidu.html">        </iframe>    </div></p><p>    <div style="width:820px; height:1000px;border:none;text-align:center">        <iframe             allowtransparency="yes"             frameborder="0"             width="820px"             height="1000px"             scrolling="auto"             style="box-shadow: 0px 0px 20px -10px #888;"             src="/html/qinhuangdao_baidu.html" >        </iframe>    </div></p> -->]]></content>
    
    
      
      
    <summary type="html">&lt;script src=&quot;/assets/asciinema-player.js&quot;&gt;&lt;/script&gt;&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;/assets/asciinema-player.css&quot; /&gt;
&lt;p&gt;&lt;a href=</summary>
      
    
    
    
    
  </entry>
  
  <entry>
    <title>开发环境搭建视频</title>
    <link href="https://wangmingco.github.io/2021/06/09/html/videos/"/>
    <id>https://wangmingco.github.io/2021/06/09/html/videos/</id>
    <published>2021-06-09T15:40:00.000Z</published>
    <updated>2025-12-19T01:50:58.670Z</updated>
    
    <content type="html"><![CDATA[<script src="/assets/asciinema-player.js"></script><link rel="stylesheet" type="text/css" href="/assets/asciinema-player.css" /><p><a href="/html/video.html">开发环境视频</a></p><p>在命令行中设置go的开发环境</p><div id="dplayer0" class="dplayer hexo-tag-dplayer-mark" style="margin-bottom: 20px;"></div><script>(function(){var player = new DPlayer({"container":document.getElementById("dplayer0"),"theme":"#FADFA3","video":{"url":"https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/videos/command-go-dev.mp4?raw=true"},"danmaku":{"api":"https://api.prprpr.me/dplayer/"}});window.dplayers||(window.dplayers=[]);window.dplayers.push(player);})()</script> <p>基于 .Net 开发API 服务</p><div id="dplayer1" class="dplayer hexo-tag-dplayer-mark" style="margin-bottom: 20px;"></div><script>(function(){var player = new DPlayer({"container":document.getElementById("dplayer1"),"theme":"#FADFA3","video":{"url":"https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/videos/dotnet_api_server.mp4"},"danmaku":{"api":"https://api.prprpr.me/dplayer/"}});window.dplayers||(window.dplayers=[]);window.dplayers.push(player);})()</script> <p>Goland 开发环境设置</p><div id="dplayer2" class="dplayer hexo-tag-dplayer-mark" style="margin-bottom: 20px;"></div><script>(function(){var player = new DPlayer({"container":document.getElementById("dplayer2"),"theme":"#FADFA3","video":{"url":"https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/videos/goland-dev.mp4"},"danmaku":{"api":"https://api.prprpr.me/dplayer/"}});window.dplayers||(window.dplayers=[]);window.dplayers.push(player);})()</script> <p>idea 插件开发环境搭建</p><div id="dplayer3" class="dplayer hexo-tag-dplayer-mark" style="margin-bottom: 20px;"></div><script>(function(){var player = new DPlayer({"container":document.getElementById("dplayer3"),"theme":"#FADFA3","video":{"url":"https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/videos/idea_plugin.mp4"},"danmaku":{"api":"https://api.prprpr.me/dplayer/"}});window.dplayers||(window.dplayers=[]);window.dplayers.push(player);})()</script> <p>基于idea 进行性能剖析</p><div id="dplayer4" class="dplayer hexo-tag-dplayer-mark" style="margin-bottom: 20px;"></div><script>(function(){var player = new DPlayer({"container":document.getElementById("dplayer4"),"theme":"#FADFA3","video":{"url":"https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/videos/idea_profiler.mp4"},"danmaku":{"api":"https://api.prprpr.me/dplayer/"}});window.dplayers||(window.dplayers=[]);window.dplayers.push(player);})()</script> <p>基于VSCode 设置Docker开发环境</p><div id="dplayer5" class="dplayer hexo-tag-dplayer-mark" style="margin-bottom: 20px;"></div><script>(function(){var player = new DPlayer({"container":document.getElementById("dplayer5"),"theme":"#FADFA3","video":{"url":"https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/videos/vs_docker.mp4"},"danmaku":{"api":"https://api.prprpr.me/dplayer/"}});window.dplayers||(window.dplayers=[]);window.dplayers.push(player);})()</script> <p>基于VSCode设置.Net开发环境</p><div id="dplayer6" class="dplayer hexo-tag-dplayer-mark" style="margin-bottom: 20px;"></div><script>(function(){var player = new DPlayer({"container":document.getElementById("dplayer6"),"theme":"#FADFA3","video":{"url":"https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/videos/vscode_dotnet.mp4"},"danmaku":{"api":"https://api.prprpr.me/dplayer/"}});window.dplayers||(window.dplayers=[]);window.dplayers.push(player);})()</script> <p>基于VSCode远程开发</p><div id="dplayer7" class="dplayer hexo-tag-dplayer-mark" style="margin-bottom: 20px;"></div><script>(function(){var player = new DPlayer({"container":document.getElementById("dplayer7"),"theme":"#FADFA3","video":{"url":"https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/videos/vscode_remote.mp4"},"danmaku":{"api":"https://api.prprpr.me/dplayer/"}});window.dplayers||(window.dplayers=[]);window.dplayers.push(player);})()</script> <p>开发Mac APP</p><div id="dplayer8" class="dplayer hexo-tag-dplayer-mark" style="margin-bottom: 20px;"></div><script>(function(){var player = new DPlayer({"container":document.getElementById("dplayer8"),"theme":"#FADFA3","video":{"url":"https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/videos/xcode_menu_bar_app.mp4"},"danmaku":{"api":"https://api.prprpr.me/dplayer/"}});window.dplayers||(window.dplayers=[]);window.dplayers.push(player);})()</script> <p>Mac下使用VSCode搭建scheme/lisp 开发环境</p><div id="dplayer9" class="dplayer hexo-tag-dplayer-mark" style="margin-bottom: 20px;"></div><script>(function(){var player = new DPlayer({"container":document.getElementById("dplayer9"),"theme":"#FADFA3","video":{"url":"https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/videos/65271391-1-208.mp4"},"danmaku":{"api":"https://api.prprpr.me/dplayer/"}});window.dplayers||(window.dplayers=[]);window.dplayers.push(player);})()</script> <p>Mac下使用VSCode搭建Common Lisp 开发环境</p><div id="dplayer10" class="dplayer hexo-tag-dplayer-mark" style="margin-bottom: 20px;"></div><script>(function(){var player = new DPlayer({"container":document.getElementById("dplayer10"),"theme":"#FADFA3","video":{"url":"https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/videos/65582794-1-208.mp4"},"danmaku":{"api":"https://api.prprpr.me/dplayer/"}});window.dplayers||(window.dplayers=[]);window.dplayers.push(player);})()</script> ]]></content>
    
    
      
      
    <summary type="html">&lt;script src=&quot;/assets/asciinema-player.js&quot;&gt;&lt;/script&gt;&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;/assets/asciinema-player.css&quot; /&gt;
&lt;p&gt;&lt;a href=</summary>
      
    
    
    
    
  </entry>
  
  <entry>
    <title>使用特定的SSH Key提交GIT</title>
    <link href="https://wangmingco.github.io/2020/11/24/%E6%9D%82%E6%96%87/git-ssh/"/>
    <id>https://wangmingco.github.io/2020/11/24/%E6%9D%82%E6%96%87/git-ssh/</id>
    <published>2020-11-24T20:43:00.000Z</published>
    <updated>2025-12-19T01:50:58.674Z</updated>
    
    <content type="html"><![CDATA[<script src="/assets/asciinema-player.js"></script><link rel="stylesheet" type="text/css" href="/assets/asciinema-player.css" /><ol><li>生成新的 ssh key，避免替换新的 sshkey</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ssh-keygen -C &quot;xxx@hotmail.com&quot; -f ~/.ssh/id_rsa_gitee -P &quot;&quot;</span><br></pre></td></tr></table></figure><ol start="2"><li>在 /Users/xxx/.ssh/config 文件里添加新的新的 Host</li></ol><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></pre></td><td class="code"><pre><span class="line">Host gitee.com</span><br><span class="line">    HostName gitee.com</span><br><span class="line">    User git</span><br><span class="line">    IdentityFile /Users/xxx/.ssh/id_rsa_gitee</span><br><span class="line">    IdentitiesOnly yes</span><br></pre></td></tr></table></figure><ol start="3"><li>进入到仓库里，在 git 仓库里设置用户名密码</li></ol><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">git config user.email &quot;xxx@hotmail.com&quot;</span><br><span class="line">git config user.name &quot;xxx&quot;</span><br></pre></td></tr></table></figure><ol start="4"><li>添加远程仓库</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git remote add backup git@gitee.com:xxx-backup/xxx.201124.git</span><br></pre></td></tr></table></figure><ol start="5"><li>测试</li></ol><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></pre></td><td class="code"><pre><span class="line">➜  xxx git:(master) ✗ git push backup</span><br><span class="line">枚举对象: 478, 完成.</span><br><span class="line">对象计数中: 100% (478/478), 完成.</span><br><span class="line">使用 8 个线程进行压缩</span><br><span class="line">压缩对象中: 100% (324/324), 完成.</span><br><span class="line">写入对象中: 100% (478/478), 2.78 MiB | 5.84 MiB/s, 完成.</span><br><span class="line">总共 478 （差异 147），复用 0 （差异 0）</span><br><span class="line">remote: Resolving deltas: 100% (147/147), done.</span><br><span class="line">remote: Powered by GITEE.COM [GNK-5.0]</span><br><span class="line">To gitee.com:xxx-backup/xxx.201124.git</span><br><span class="line"> * [new branch]      master -&gt; master</span><br></pre></td></tr></table></figure><p>上传脚本</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">#</span><span class="language-bash">!/usr/bin/env bash</span></span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">Get the repos absolute path recursively</span></span><br><span class="line">email=&quot;xxx@hotmail.com&quot;</span><br><span class="line">pwd=&quot;xxx&quot;</span><br><span class="line">cid=&quot;c65e97ab88de932e7c23e9d4b563e5c99b5926a06e26d2e8af26885bd5a6b1da&quot;</span><br><span class="line">csr=&quot;729aeae125864b5577a82629296ce7e389aab8bee5384c25a9fa2c2572064374&quot;</span><br><span class="line">username=&quot;xxx-backup&quot;</span><br><span class="line"></span><br><span class="line">get_repo_paths() &#123;</span><br><span class="line">    find . -type d -name &quot;.git&quot;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">get_token() &#123;</span><br><span class="line">    curl -q -X POST --data-urlencode &quot;grant_type=password&quot; --data-urlencode &quot;username=$&#123;email&#125;&quot; --data-urlencode &quot;password=$&#123;pwd&#125;&quot; --data-urlencode &quot;client_id=$&#123;cid&#125;&quot; --data-urlencode &quot;client_secret=$&#123;csr&#125;&quot; --data-urlencode &quot;scope=projects user_info issues notes&quot; https://gitee.com/oauth/token | jq -r &quot;.access_token&quot;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">push() &#123;</span><br><span class="line">    cd &quot;$1&quot;</span><br><span class="line">    local datetime=`date +%y%m%d`</span><br><span class="line">    local git_path=&quot;$&#123;PWD&#125;&quot;</span><br><span class="line">    local local_path=&quot;$&#123;git_path%/.git*&#125;&quot;</span><br><span class="line">    local repo_name=&quot;$&#123;local_path##*/&#125;&quot;</span><br><span class="line">    local remote_path=&quot;git@gitee.com:$&#123;username&#125;/$&#123;repo_name&#125;.git&quot;</span><br><span class="line">    local http_path=&quot;https://gitee.com/$&#123;username&#125;/$&#123;repo_name&#125;.git&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    cd &quot;$&#123;local_path&#125;&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    echo &quot;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=&quot;</span><br><span class="line">    echo &quot;&gt; \`$&#123;repo_name&#125;&#x27;&quot;</span><br><span class="line">    echo &quot;  Local path : $PWD&quot;</span><br><span class="line">    echo &quot;  Remote path: $remote_path&quot;</span><br><span class="line">    echo &quot;  Http path: $http_path&quot;</span><br><span class="line">    echo &quot;  Token: $&#123;access_token&#125;&quot;</span><br><span class="line">    echo &quot;  RepoName: $&#123;repo_name&#125;&quot;</span><br><span class="line">    echo &quot;= = = = = = = = = = = = = = = = = = = = = = = = = = =&quot;</span><br><span class="line">    echo &quot;&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    curl -X POST -s\</span><br><span class="line">         --header &#x27;Content-Type: application/json;charset=UTF-8&#x27; &#x27;https://gitee.com/api/v5/user/repos&#x27;\</span><br><span class="line">         -d &#x27;&#123;&quot;access_token&quot;:&quot;&#x27;$&#123;access_token&#125;&#x27;&quot;,&quot;name&quot;:&quot;&#x27;$&#123;repo_name&#125;&#x27;&quot;,&quot;has_issues&quot;:&quot;true&quot;,&quot;has_wiki&quot;:&quot;true&quot;,&quot;can_comment&quot;:&quot;true&quot;, &quot;private&quot;:&quot;true&quot;&#125;&#x27;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    git config user.email &quot;xxx@hotmail.com&quot;</span><br><span class="line">    git config user.name &quot;xxx&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    git remote rm backup</span><br><span class="line">    git remote add backup &quot;$remote_path&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    git push backup</span><br><span class="line">    return_status=$?</span><br><span class="line"></span><br><span class="line">    echo &quot;= = = = = = = = = = = = = = = = = = = = = = = = = = =&quot;</span><br><span class="line"></span><br><span class="line">    return $return_status</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">die() &#123;</span><br><span class="line">    echo &quot;&gt; Fatal error!!!&quot;</span><br><span class="line">    exit 1</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">main() &#123;</span><br><span class="line">    access_token=$(get_token)</span><br><span class="line"></span><br><span class="line">    get_repo_paths | while read p; do</span><br><span class="line">        push &quot;$&#123;p&#125;&quot; || die</span><br><span class="line">    done</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">main</span><br><span class="line"></span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;script src=&quot;/assets/asciinema-player.js&quot;&gt;&lt;/script&gt;&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;/assets/asciinema-player.css&quot; /&gt;
&lt;ol&gt;
&lt;li&gt;生成</summary>
      
    
    
    
    <category term="杂文" scheme="https://wangmingco.github.io/categories/%E6%9D%82%E6%96%87/"/>
    
    
  </entry>
  
  <entry>
    <title>HttpClient 多线程请求</title>
    <link href="https://wangmingco.github.io/2020/11/05/Java/lib/HttpClient%20%E5%A4%9A%E7%BA%BF%E7%A8%8B%E8%AF%B7%E6%B1%82/"/>
    <id>https://wangmingco.github.io/2020/11/05/Java/lib/HttpClient%20%E5%A4%9A%E7%BA%BF%E7%A8%8B%E8%AF%B7%E6%B1%82/</id>
    <published>2020-11-05T00:00:00.000Z</published>
    <updated>2025-12-19T01:50:58.666Z</updated>
    
    <content type="html"><![CDATA[<script src="/assets/asciinema-player.js"></script><link rel="stylesheet" type="text/css" href="/assets/asciinema-player.css" /><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><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br></pre></td><td class="code"><pre><span class="line">public class HttpClient &#123;</span><br><span class="line"></span><br><span class="line">   private static final Logger LOGGER = LoggerFactory.getLogger(HttpClient.class);</span><br><span class="line"></span><br><span class="line">   private static final CloseableHttpClient staticClient;</span><br><span class="line">   static &#123;</span><br><span class="line">      RequestConfig requestConfig = RequestConfig.custom()</span><br><span class="line">            .setSocketTimeout(8000)</span><br><span class="line">            .setConnectTimeout(8000)</span><br><span class="line">            .build();</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">      staticClient = HttpClients.custom()</span><br><span class="line">            .setDefaultRequestConfig(requestConfig)</span><br><span class="line">            .useSystemProperties()</span><br><span class="line">            .build();</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">   public static void main(String[] args) throws InterruptedException &#123;</span><br><span class="line"></span><br><span class="line">      TimeUnit.SECONDS.sleep(10);</span><br><span class="line"></span><br><span class="line">      for (int i = 1; i &lt; 20; i++) &#123;</span><br><span class="line">         startRequest(i);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">   private static void startRequest(int idx) &#123;</span><br><span class="line">      new Thread(() -&gt; &#123;</span><br><span class="line"></span><br><span class="line">         HttpHost httpHost = new HttpHost(&quot;localhost&quot;, 8080);</span><br><span class="line">         HttpRequest httpRequest = new HttpGet(&quot;/&quot; + idx);</span><br><span class="line">         try &#123;</span><br><span class="line">            CloseableHttpClient httpClient = getClient();</span><br><span class="line">            print(httpClient);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">            LOGGER.info(&quot;请求开始: &quot; + idx);</span><br><span class="line">            httpClient.execute(httpHost, httpRequest);</span><br><span class="line">            LOGGER.info(&quot;请求结束: &quot; + idx);</span><br><span class="line">         &#125; catch (IOException e) &#123;</span><br><span class="line">            LOGGER.error(&quot;请求异常: &quot; + idx, e);</span><br><span class="line">         &#125;</span><br><span class="line">      &#125;).start();</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">   private static CloseableHttpClient getClient() &#123;</span><br><span class="line">      return staticClient;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   private static void print(CloseableHttpClient httpClient) &#123;</span><br><span class="line">      try &#123;</span><br><span class="line"></span><br><span class="line">         Field field = httpClient.getClass().getDeclaredField(&quot;connManager&quot;);</span><br><span class="line">         field.setAccessible(true);</span><br><span class="line"></span><br><span class="line">         PoolingHttpClientConnectionManager clientConnectionManager = (PoolingHttpClientConnectionManager)field.get(httpClient);</span><br><span class="line">         PoolStats stats = clientConnectionManager.getTotalStats();</span><br><span class="line">         System.out.println(&quot;Max : &quot; + stats.getMax()</span><br><span class="line">               + &quot;. Available : &quot; + stats.getAvailable()</span><br><span class="line">               + &quot;. Leased : &quot; + stats.getLeased()</span><br><span class="line">               + &quot;. Pending : &quot; + stats.getPending()</span><br><span class="line">               + &quot;. DefaultMaxPerRoute : &quot; + clientConnectionManager.getDefaultMaxPerRoute()</span><br><span class="line">               + &quot;. MaxTotal : &quot; + clientConnectionManager.getMaxTotal()</span><br><span class="line">         );</span><br><span class="line"></span><br><span class="line">         field.setAccessible(false);</span><br><span class="line">      &#125; catch (Exception 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>服务端沉睡3秒钟，每次执行这段代码的话，都只会有2个请求执行完成。<br>这是因为在<code>HttpClient</code>的连接池中<code>MaxtTotal</code>， <code>DefaultMaxPerRoute</code>有这么俩个参数，</p><ul><li><code>MaxtTotal</code> 表示连接池中最大的连接数，</li><li><code>DefaultMaxPerRoute</code> 表示某个地址最大的并发连接数。</li></ul><p>比如<code>MaxtTotal</code>为20，<code>DefaultMaxPerRoute</code>为2（这俩个值也是默认值）。<br>现在有10个并发请求，其中4个请求<a href="http://www.baidu.com/">www.baidu.com</a>, 6个请求<a href="http://www.qq.com/">www.qq.com</a>, 如果服务器都阻塞住的话，那么现在的并发连接是4个，baidu2个，qq俩个。<br>如果在<code>RequestConfig#setConnectionRequestTimeout</code>这个没有设置的话，其他的请求线程就会一直阻塞了。</p><p>解决这个问题的话，有三种方案</p><ol><li>设置<code>RequestConfig#setConnectionRequestTimeout</code>这个值，大于0的话，如果到达时间没有可用连接的话，就会抛出异常</li><li>设置<code>System.setProperty(&quot;http.maxConnections&quot;, &quot;20”);</code> 这个值会调大 <code>DefaultMaxPerRoute</code> 这个值</li><li>不要共享<code>HttpClient</code>，如果想要降低new的话，可以使用<code>ThreadLocal</code></li></ol>]]></content>
    
    
      
      
    <summary type="html">&lt;script src=&quot;/assets/asciinema-player.js&quot;&gt;&lt;/script&gt;&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;/assets/asciinema-player.css&quot; /&gt;
&lt;p&gt;写一段客户端测试</summary>
      
    
    
    
    <category term="Java" scheme="https://wangmingco.github.io/categories/Java/"/>
    
    
    <category term="Java 三方库" scheme="https://wangmingco.github.io/tags/Java-%E4%B8%89%E6%96%B9%E5%BA%93/"/>
    
  </entry>
  
  <entry>
    <title>ssh 防止自动断开</title>
    <link href="https://wangmingco.github.io/2020/11/03/%E6%9D%82%E6%96%87/ssh%20%E9%98%B2%E6%AD%A2%E8%87%AA%E5%8A%A8%E6%96%AD%E5%BC%80/"/>
    <id>https://wangmingco.github.io/2020/11/03/%E6%9D%82%E6%96%87/ssh%20%E9%98%B2%E6%AD%A2%E8%87%AA%E5%8A%A8%E6%96%AD%E5%BC%80/</id>
    <published>2020-11-03T00:00:00.000Z</published>
    <updated>2025-12-19T01:50:58.674Z</updated>
    
    <content type="html"><![CDATA[<script src="/assets/asciinema-player.js"></script><link rel="stylesheet" type="text/css" href="/assets/asciinema-player.css" /><p>1、$TMOUT 系统环境变量</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></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">用以下命令判断是否是否设置了该参数</span></span><br><span class="line">echo $TMOUT</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">如果输出空或0表示不超时，大于0的数字n表示n秒没有收入则超时</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">修改方法</span></span><br><span class="line">vi /etc/profile</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">----------------------------</span></span><br><span class="line">export TMOUT=900</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">----------------------------</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">将以上900修改为0就是设置不超时</span></span><br><span class="line">source /etc/profile</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">让配置立即生效</span></span><br></pre></td></tr></table></figure><p>2、sshd 服务配置</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></pre></td><td class="code"><pre><span class="line">cd /etc/ssh</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看sshd_config中关于客户端活动状态的配置</span></span><br><span class="line">grep ClientAlive sshd_config</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">默认配置如下</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">----------------------------</span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">ClientAliveInterval 0</span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">ClientAliveCountMax 3</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">----------------------------</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">ClientAliveInterval指定了服务器端向客户端请求消息的时间间隔, 默认是0, 不发送。设置60表示每分钟发送一次, 然后客户端响应, 这样就保持长连接了。</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">ClientAliveCountMax表示服务器发出请求后客户端没有响应的次数达到一定值, 就自动断开。正常情况下, 客户端不会不响应，使用默认值3即可。</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">备份原配置文件</span></span><br><span class="line">cp sshd_config sshd_config.bak</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">启用客户端活动检查，每60秒检查一次，3次不活动断开连接</span></span><br><span class="line">sed -i &quot;s/#ClientAliveInterval 0/ClientAliveInterval 60/g&quot; sshd_config</span><br><span class="line">sed -i &quot;s/#ClientAliveCountMax 3/ClientAliveCountMax 3/g&quot; sshd_config</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">确认修改</span></span><br><span class="line">grep ClientAlive sshd_config</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">比较配置文件差异</span></span><br><span class="line">diff sshd_config sshd_config.bak</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">重新加载ssd配置，让配置生效</span></span><br><span class="line">service sshd reload</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;script src=&quot;/assets/asciinema-player.js&quot;&gt;&lt;/script&gt;&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;/assets/asciinema-player.css&quot; /&gt;
&lt;p&gt;1、$TMOUT</summary>
      
    
    
    
    <category term="杂文" scheme="https://wangmingco.github.io/categories/%E6%9D%82%E6%96%87/"/>
    
    
  </entry>
  
  <entry>
    <title>Java 线程状态</title>
    <link href="https://wangmingco.github.io/2020/05/13/Java/javase/Java%20%E7%BA%BF%E7%A8%8B%E7%8A%B6%E6%80%81/"/>
    <id>https://wangmingco.github.io/2020/05/13/Java/javase/Java%20%E7%BA%BF%E7%A8%8B%E7%8A%B6%E6%80%81/</id>
    <published>2020-05-13T00:00:00.000Z</published>
    <updated>2025-12-19T01:50:58.658Z</updated>
    
    <content type="html"><![CDATA[<script src="/assets/asciinema-player.js"></script><link rel="stylesheet" type="text/css" href="/assets/asciinema-player.css" /><blockquote><p><a href="https://zhuanlan.zhihu.com/p/140396504">Java 线程状态</a></p></blockquote><p>Java装状态流转如下</p><p><img src="https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/images/javase/java_thread_state1.jpg"></p><p>Running这个状态在Java平台中其实是不存在的，图中表示的只是获取了CPU的RUNNABLE状态。上图只是给出了一个大概的流程运转图，图里的有些操作是需要多个线程配合才能完成的，具体的流转过程在下面有详细的解释</p><p>Java线程状态在<code>java.lang.Thread.State</code> 这个枚举类中有描述</p><ul><li><code>NEW</code>：表示还未开始执行的状态RUNNABLE：表示线程是可运行，但目前等待系统运行资源（例如CPU等）</li><li><code>BLOCKED</code>：表示线程目前正在等待monitor lock，被阻塞住了。这种状态下是处于进入<code>synchronized block/method</code> 或者 调用<code>Object.wait()</code>之后重新进入 <code>synchronized block/method</code> 。</li><li><code>WAITING</code>：线程A处于等待操作，等待线程B执行一个特定的操作。当线程A调用了<code>Object.wait()/ Thread.join()/ LockSupport.park()</code> 这些方法后就会进入 <code>WAITING</code>状态</li><li><code>TIMED_WAITING</code>：线程处于一个带有超时时间的 WAITING 状态。当线程A调用了 <code>Thread.sleep()/ Object.wait()/ Thread.join()/ LockSupport.parkNanos()/ LockSupport.parkUntil()</code> 这些方法后就会进入 <code>TIMED_WAITING</code> 状态。</li><li><code>TERMINATED</code>： 表示线程执行完成了。</li></ul><p>Java线程的状态不多，主要的也就是 <code>BLOCKED/WAITING/TIMED_WAITING</code> 这三个状态。</p><p><img src="https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/images/javase/java_thread_state2.jpg"></p><p>假设我们现在有AB俩个线程，刚开始这俩个线程都是处于RUNNABLE状态，假设都是运行状态（具体是否在运行取决于CPU调度）。然后俩个线程都要进入一段同步代码块，A成功获取到了监视锁，它进入了代码块继续处于RUNNABLE状态等待CPU调度。但是B获取监视锁失败了，于是就进入了<code>BLOCKED</code>状态，等待A释放监视锁。</p><p><img src="https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/images/javase/java_thread_state3.jpg"></p><p>如果我们让A在同步块中执行监视锁对象的<code>wait()</code> 方法，A就会进入<code>WAITING</code>状态，释放监视锁等待B唤醒。B获得锁进入同步块后，调用监视锁对象的<code>notify()</code>方法唤醒A，此时A应该有一个短暂的RUNNABLE状态，然后进入进入<code>BLOCKED</code>状态等待B释放锁。</p><p>上面是Java里面定义的线程的状态, 那该状态是怎么来的呢?在源码中</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> State java.lang.Thread.getState() &#123;</span><br><span class="line">    <span class="comment">// get current thread state</span></span><br><span class="line">    <span class="keyword">return</span> sun.misc.VM.toThreadState(threadStatus);</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> Thread.State sun.misc.VM.toThreadState(<span class="type">int</span> threadStatus) &#123;</span><br><span class="line">    <span class="keyword">if</span> ((threadStatus &amp; JVMTI_THREAD_STATE_RUNNABLE) != <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> RUNNABLE;</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> ((threadStatus &amp; JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER) != <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> BLOCKED;</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> ((threadStatus &amp; JVMTI_THREAD_STATE_WAITING_INDEFINITELY) != <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> WAITING;</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> ((threadStatus &amp; JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT) != <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> TIMED_WAITING;</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> ((threadStatus &amp; JVMTI_THREAD_STATE_TERMINATED) != <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> TERMINATED;</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> ((threadStatus &amp; JVMTI_THREAD_STATE_ALIVE) == <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> NEW;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> RUNNABLE;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们发现<code>java.lang.Thread.State</code>是<code>java.lang.Thread#threadStatus</code>与JVMTI_THREAD_STATE 进行与计算得来的</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">enum</span> <span class="title class_">ThreadStatus</span> &#123;</span><br><span class="line">    NEW                      = <span class="number">0</span>,</span><br><span class="line">    RUNNABLE                 = JVMTI_THREAD_STATE_ALIVE +          <span class="comment">// runnable / running</span></span><br><span class="line">                               JVMTI_THREAD_STATE_RUNNABLE,</span><br><span class="line">    SLEEPING                 = JVMTI_THREAD_STATE_ALIVE +          <span class="comment">// Thread.sleep()</span></span><br><span class="line">                               JVMTI_THREAD_STATE_WAITING +</span><br><span class="line">                               JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT +</span><br><span class="line">                               JVMTI_THREAD_STATE_SLEEPING,</span><br><span class="line">    IN_OBJECT_WAIT           = JVMTI_THREAD_STATE_ALIVE +          <span class="comment">// Object.wait()</span></span><br><span class="line">                               JVMTI_THREAD_STATE_WAITING +</span><br><span class="line">                               JVMTI_THREAD_STATE_WAITING_INDEFINITELY +</span><br><span class="line">                               JVMTI_THREAD_STATE_IN_OBJECT_WAIT,</span><br><span class="line">    IN_OBJECT_WAIT_TIMED     = JVMTI_THREAD_STATE_ALIVE +          <span class="comment">// Object.wait(long)</span></span><br><span class="line">                               JVMTI_THREAD_STATE_WAITING +</span><br><span class="line">                               JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT +</span><br><span class="line">                               JVMTI_THREAD_STATE_IN_OBJECT_WAIT,</span><br><span class="line">    PARKED                   = JVMTI_THREAD_STATE_ALIVE +          <span class="comment">// LockSupport.park()</span></span><br><span class="line">                               JVMTI_THREAD_STATE_WAITING +</span><br><span class="line">                               JVMTI_THREAD_STATE_WAITING_INDEFINITELY +</span><br><span class="line">                               JVMTI_THREAD_STATE_PARKED,</span><br><span class="line">    PARKED_TIMED             = JVMTI_THREAD_STATE_ALIVE +          <span class="comment">// LockSupport.park(long)</span></span><br><span class="line">                               JVMTI_THREAD_STATE_WAITING +</span><br><span class="line">                               JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT +</span><br><span class="line">                               JVMTI_THREAD_STATE_PARKED,</span><br><span class="line">    BLOCKED_ON_MONITOR_ENTER = JVMTI_THREAD_STATE_ALIVE +          <span class="comment">// (re-)entering a synchronization block</span></span><br><span class="line">                               JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER,</span><br><span class="line">    TERMINATED               = JVMTI_THREAD_STATE_TERMINATED</span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">enum</span> &#123;</span><br><span class="line">    JVMTI_THREAD_STATE_ALIVE = <span class="number">0x0001</span>,</span><br><span class="line">    JVMTI_THREAD_STATE_TERMINATED = <span class="number">0x0002</span>,</span><br><span class="line">    JVMTI_THREAD_STATE_RUNNABLE = <span class="number">0x0004</span>,</span><br><span class="line">    JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER = <span class="number">0x0400</span>,</span><br><span class="line">    JVMTI_THREAD_STATE_WAITING = <span class="number">0x0080</span>,</span><br><span class="line">    JVMTI_THREAD_STATE_WAITING_INDEFINITELY = <span class="number">0x0010</span>,</span><br><span class="line">    JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT = <span class="number">0x0020</span>,</span><br><span class="line">    JVMTI_THREAD_STATE_SLEEPING = <span class="number">0x0040</span>,</span><br><span class="line">    JVMTI_THREAD_STATE_IN_OBJECT_WAIT = <span class="number">0x0100</span>,</span><br><span class="line">    JVMTI_THREAD_STATE_PARKED = <span class="number">0x0200</span>,</span><br><span class="line">    JVMTI_THREAD_STATE_SUSPENDED = <span class="number">0x100000</span>,</span><br><span class="line">    JVMTI_THREAD_STATE_INTERRUPTED = <span class="number">0x200000</span>,</span><br><span class="line">    JVMTI_THREAD_STATE_IN_NATIVE = <span class="number">0x400000</span>,</span><br><span class="line">    JVMTI_THREAD_STATE_VENDOR_1 = <span class="number">0x10000000</span>,</span><br><span class="line">    JVMTI_THREAD_STATE_VENDOR_2 = <span class="number">0x20000000</span>,</span><br><span class="line">    JVMTI_THREAD_STATE_VENDOR_3 = <span class="number">0x40000000</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>通过源码看<code>java.lang.Thread</code>中并没有对<code>threadStatus</code> 赋值的操作，那应该就是jvm赋值的</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"></span><br></pre></td></tr></table></figure><h2 id="jvisualvm线程状态颜色"><a href="#jvisualvm线程状态颜色" class="headerlink" title="jvisualvm线程状态颜色"></a>jvisualvm线程状态颜色</h2><h3 id="监视状态"><a href="#监视状态" class="headerlink" title="监视状态"></a>监视状态</h3><p>进入锁 BLOCKED</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TestInSynchronized</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> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        TimeUnit.SECONDS.sleep(<span class="number">8</span>);</span><br><span class="line"></span><br><span class="line">        <span class="type">Object</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line">        <span class="type">AtomicInteger</span> <span class="variable">atomicInteger</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AtomicInteger</span>();</span><br><span class="line"></span><br><span class="line">        <span class="type">Runnable</span> <span class="variable">runnable</span> <span class="operator">=</span> () -&gt; &#123;</span><br><span class="line">            <span class="keyword">synchronized</span> (lock) &#123;</span><br><span class="line">                <span class="type">long</span> <span class="variable">start</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">                <span class="type">int</span> <span class="variable">sec</span> <span class="operator">=</span> atomicInteger.addAndGet(<span class="number">2</span>);</span><br><span class="line">                System.out.println(Thread.currentThread().getName() + <span class="string">&quot; running : &quot;</span> + sec + <span class="string">&quot; seconds&quot;</span>);</span><br><span class="line">                <span class="keyword">while</span> ((System.currentTimeMillis() - start) &lt; sec * <span class="number">1000</span>) &#123;&#125;</span><br><span class="line">                System.out.println(Thread.currentThread().getName() + <span class="string">&quot; running finish&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line">        <span class="type">Thread</span> <span class="variable">thread1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(runnable, <span class="string">&quot;Flying Thread&quot;</span>);</span><br><span class="line">        <span class="type">Thread</span> <span class="variable">thread2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(runnable, <span class="string">&quot;Running Thread&quot;</span>);</span><br><span class="line"></span><br><span class="line">        thread1.start();</span><br><span class="line">        thread2.start();</span><br><span class="line"></span><br><span class="line">        ThreadUtil.printThreadState(thread1);</span><br><span class="line">        ThreadUtil.printThreadState(thread2);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>程序输出为</p><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></pre></td><td class="code"><pre><span class="line">Flying Thread running : 2 seconds</span><br><span class="line">Running Thread[BLOCKED] 1:-1</span><br><span class="line">Flying Thread[RUNNABLE] 0:-1</span><br><span class="line">Flying Thread[RUNNABLE] 0:-1</span><br><span class="line">Running Thread[BLOCKED] 1:-1</span><br><span class="line">Flying Thread running finish</span><br><span class="line">Running Thread running : 4 seconds</span><br><span class="line">Running Thread[RUNNABLE] 1:-1</span><br><span class="line">Running Thread[RUNNABLE] 1:-1</span><br><span class="line">Running Thread[RUNNABLE] 1:-1</span><br><span class="line">Running Thread[RUNNABLE] 1:-1</span><br><span class="line">Running Thread running finish</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/images/javase/jvisualvm_BLOCKED.png"></p><p>当线程进入锁之后，线程的状态就被切换成了监视状态。当线程运行完之后线程的颜色就变成了白色，表示运行完了</p><h3 id="等待状态"><a href="#等待状态" class="headerlink" title="等待状态"></a>等待状态</h3><p>调用wait() TIMED_WAITING</p><p>进入wait状态 调用Object.wait()方法</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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">import</span> java.util.concurrent.TimeUnit;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TestThreadInWait</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> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        TimeUnit.SECONDS.sleep(<span class="number">8</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">final</span> <span class="type">Object</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line"></span><br><span class="line">        <span class="type">Thread</span> <span class="variable">thread</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">            ThreadUtil.running(<span class="number">2</span>);</span><br><span class="line">            <span class="keyword">synchronized</span> (lock) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    lock.wait(<span class="number">3000</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">            ThreadUtil.running(<span class="number">2</span>);</span><br><span class="line">        &#125;, <span class="string">&quot;User&quot;</span>);</span><br><span class="line"></span><br><span class="line">        thread.start();</span><br><span class="line"></span><br><span class="line">        ThreadUtil.printThreadState(thread);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>输出</p><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></pre></td><td class="code"><pre><span class="line">2017-09-28T12:16:19.669  User[RUNNABLE] 2:-1</span><br><span class="line">2017-09-28T12:16:20.646  User[RUNNABLE] 2:-1</span><br><span class="line">2017-09-28T12:16:21.646  User[TIMED_WAITING] 2:-1</span><br><span class="line">2017-09-28T12:16:22.647  User[TIMED_WAITING] 2:-1</span><br><span class="line">2017-09-28T12:16:23.647  User[TIMED_WAITING] 2:-1</span><br><span class="line">2017-09-28T12:16:24.647  User[RUNNABLE] 2:-1</span><br><span class="line">2017-09-28T12:16:25.647  User[RUNNABLE] 2:-1</span><br><span class="line">2017-09-28T12:16:26.644  User[RUNNABLE] 2:-1</span><br></pre></td></tr></table></figure><p>当线程进入wait时, 并不是阻塞并不会计数,waitedCount一直在增长</p><p><img src="https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/images/javase/jvisualvm_TIMED_WAITING.png"></p><h3 id="休眠状态"><a href="#休眠状态" class="headerlink" title="休眠状态"></a>休眠状态</h3><p>Thread.sleep()  TIMED_WAITING</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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">import</span> java.util.concurrent.TimeUnit;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TestStateInSleep</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> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        TimeUnit.SECONDS.sleep(<span class="number">8</span>);</span><br><span class="line"></span><br><span class="line">        <span class="type">Thread</span> <span class="variable">thread</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">            ThreadUtil.running(<span class="number">2</span>);</span><br><span class="line"></span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                TimeUnit.SECONDS.sleep(<span class="number">2</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><br><span class="line">            ThreadUtil.running(<span class="number">1</span>);</span><br><span class="line">        &#125;, <span class="string">&quot;User&quot;</span>);</span><br><span class="line">        thread.start();</span><br><span class="line"></span><br><span class="line">        ThreadUtil.printThreadState(thread);</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></pre></td><td class="code"><pre><span class="line">2017-09-28T12:13:23.309  User[RUNNABLE] 3:-1</span><br><span class="line">2017-09-28T12:13:24.287  User[RUNNABLE] 3:-1</span><br><span class="line">2017-09-28T12:13:25.287  User[TIMED_WAITING] 3:-1</span><br><span class="line">2017-09-28T12:13:26.287  User[TIMED_WAITING] 3:-1</span><br><span class="line">2017-09-28T12:13:27.288  User[RUNNABLE] 3:-1</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/images/javase/jvisualvm_TIMED_WAITING1.png"></p><h3 id="Join"><a href="#Join" class="headerlink" title="Join"></a>Join</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.concurrent.TimeUnit;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ThreadJoin</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> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        TimeUnit.SECONDS.sleep(<span class="number">6</span>);</span><br><span class="line"></span><br><span class="line">        <span class="type">Thread</span> <span class="variable">thread</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">            ThreadUtil.running(<span class="number">2</span>);</span><br><span class="line">        &#125;, <span class="string">&quot;User&quot;</span>);</span><br><span class="line">        thread.start();</span><br><span class="line"></span><br><span class="line">        thread.join();</span><br><span class="line">        ThreadUtil.running(<span class="number">2</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/images/javase/jvisualvm_Join.png"></p><h3 id="驻留状态"><a href="#驻留状态" class="headerlink" title="驻留状态"></a>驻留状态</h3><p>WAITING<br>Java5 Lock</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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">import</span> java.util.concurrent.TimeUnit;</span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.atomic.AtomicInteger;</span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.locks.Lock;</span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.locks.ReentrantLock;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TestInLock</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> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        TimeUnit.SECONDS.sleep(<span class="number">8</span>);</span><br><span class="line"></span><br><span class="line">        <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><br><span class="line">        <span class="type">AtomicInteger</span> <span class="variable">atomicInteger</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AtomicInteger</span>();</span><br><span class="line"></span><br><span class="line">        <span class="type">Runnable</span> <span class="variable">runnable</span> <span class="operator">=</span> () -&gt; &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                lock.lock();</span><br><span class="line">                <span class="type">int</span> <span class="variable">sec</span> <span class="operator">=</span> atomicInteger.addAndGet(<span class="number">2</span>);</span><br><span class="line">                System.out.println(Thread.currentThread().getName() + <span class="string">&quot; running : &quot;</span> + sec + <span class="string">&quot; seconds&quot;</span>);</span><br><span class="line">                ThreadUtil.running(sec);</span><br><span class="line">                System.out.println(Thread.currentThread().getName() + <span class="string">&quot; running finish&quot;</span>);</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="type">Thread</span> <span class="variable">thread1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(runnable, <span class="string">&quot;Flying Thread&quot;</span>);</span><br><span class="line">        <span class="type">Thread</span> <span class="variable">thread2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(runnable, <span class="string">&quot;Running Thread&quot;</span>);</span><br><span class="line"></span><br><span class="line">        thread1.start();</span><br><span class="line">        thread2.start();</span><br><span class="line"></span><br><span class="line">        ThreadUtil.printThreadState(thread1);</span><br><span class="line">        ThreadUtil.printThreadState(thread2);</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></pre></td><td class="code"><pre><span class="line">Flying Thread running : 2 seconds</span><br><span class="line">2017-09-28T12:29:33.728  Flying Thread[RUNNABLE] 2:-1</span><br><span class="line">2017-09-28T12:29:33.729  Running Thread[WAITING] 0:-1</span><br><span class="line">2017-09-28T12:29:34.711  Flying Thread[RUNNABLE] 2:-1</span><br><span class="line">2017-09-28T12:29:34.711  Running Thread[WAITING] 0:-1</span><br><span class="line">Flying Thread running finish</span><br><span class="line">Running Thread running : 4 seconds</span><br><span class="line">2017-09-28T12:29:35.711  Running Thread[RUNNABLE] 0:-1</span><br><span class="line">2017-09-28T12:29:36.709  Running Thread[RUNNABLE] 0:-1</span><br><span class="line">2017-09-28T12:29:37.711  Running Thread[RUNNABLE] 0:-1</span><br><span class="line">2017-09-28T12:29:38.709  Running Thread[RUNNABLE] 0:-1</span><br><span class="line">Running Thread running finish</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/images/javase/jvisualvm_WAITING.png"></p><h3 id="读取-IO"><a href="#读取-IO" class="headerlink" title="读取 IO"></a>读取 IO</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TestIO</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> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        TimeUnit.SECONDS.sleep(<span class="number">8</span>);</span><br><span class="line"></span><br><span class="line">        <span class="type">Runnable</span> <span class="variable">runnable</span> <span class="operator">=</span> () -&gt; &#123;</span><br><span class="line">            <span class="type">File</span> <span class="variable">file</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">File</span>(<span class="string">&quot;/Users/dawangyu/123.mkv&quot;</span>);</span><br><span class="line">            <span class="keyword">try</span> (<span class="type">FileInputStream</span> <span class="variable">fileInputStream</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FileInputStream</span>(file);) &#123;</span><br><span class="line">                <span class="type">byte</span>[] bytes = <span class="keyword">new</span> <span class="title class_">byte</span>[<span class="number">1024</span> * <span class="number">1024</span> * <span class="number">1024</span>];</span><br><span class="line">                fileInputStream.read(bytes);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (FileNotFoundException e) &#123;</span><br><span class="line">                e.printStackTrace();</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">        <span class="type">Thread</span> <span class="variable">thread1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(runnable, <span class="string">&quot;Flying Thread&quot;</span>);</span><br><span class="line"></span><br><span class="line">        thread1.start();</span><br><span class="line"></span><br><span class="line">        ThreadUtil.printThreadState(thread1);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>读取操作java thread 一直是运行状态</p>]]></content>
    
    
      
      
    <summary type="html">&lt;script src=&quot;/assets/asciinema-player.js&quot;&gt;&lt;/script&gt;&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;/assets/asciinema-player.css&quot; /&gt;
&lt;blockquote</summary>
      
    
    
    
    <category term="Java" scheme="https://wangmingco.github.io/categories/Java/"/>
    
    
    <category term="JavaSE" scheme="https://wangmingco.github.io/tags/JavaSE/"/>
    
  </entry>
  
  <entry>
    <title>写一个在线 Java 脚本执行器</title>
    <link href="https://wangmingco.github.io/2020/04/15/%E5%BC%80%E6%BA%90/%E5%86%99%E4%B8%80%E4%B8%AA%E5%9C%A8%E7%BA%BF%20Java%20%E8%84%9A%E6%9C%AC%E6%89%A7%E8%A1%8C%E5%99%A8/"/>
    <id>https://wangmingco.github.io/2020/04/15/%E5%BC%80%E6%BA%90/%E5%86%99%E4%B8%80%E4%B8%AA%E5%9C%A8%E7%BA%BF%20Java%20%E8%84%9A%E6%9C%AC%E6%89%A7%E8%A1%8C%E5%99%A8/</id>
    <published>2020-04-15T00:00:00.000Z</published>
    <updated>2025-12-19T01:50:58.670Z</updated>
    
    <content type="html"><![CDATA[<script src="/assets/asciinema-player.js"></script><link rel="stylesheet" type="text/css" href="/assets/asciinema-player.css" /><blockquote><p><a href="https://zhuanlan.zhihu.com/p/130425196">写一个在线 Java 脚本执行器</a></p></blockquote><p>在生产环境中，有时候我们想要快速执行一段代码，但是又不得不经历上线的痛苦（分情况哈，有时候这种痛苦是必须的）或者在某些场景中，不能重启避免破坏现场，那么有个在线脚本执行器就最好不过了。于是在工作之余，便写了这么一个 jrc 小工具 （当然市场上可能会有更好的选择，比如阿里巴巴的arthas，大家如果有更习惯的工具，也可以不参考我这个哈）</p><p>这个工具主要就是利用了java自带的javac包里的相关api实现的。先放一段效果图</p><div id="dplayer11" class="dplayer hexo-tag-dplayer-mark" style="margin-bottom: 20px;"></div><script>(function(){var player = new DPlayer({"container":document.getElementById("dplayer11"),"theme":"#FADFA3","video":{"url":"https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/videos/java_srcipt.mp4"},"danmaku":{"api":"https://api.prprpr.me/dplayer/"}});window.dplayers||(window.dplayers=[]);window.dplayers.push(player);})()</script> <p>写一个在线Java脚本执行器</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> JrcResult <span class="title function_">compile</span><span class="params">(String javaCode)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">JavaCompiler</span> <span class="variable">compiler</span> <span class="operator">=</span> ToolProvider.getSystemJavaCompiler();</span><br><span class="line">        DiagnosticCollector&lt;JavaFileObject&gt; diagnostics = <span class="keyword">new</span> <span class="title class_">DiagnosticCollector</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">        <span class="type">JrcJavaFileManager</span> <span class="variable">fileManager</span> <span class="operator">=</span> JavaFileManagerFactory.getJavaFileManager(compiler.getStandardFileManager(diagnostics, <span class="literal">null</span>, <span class="literal">null</span>));</span><br><span class="line"></span><br><span class="line">        <span class="type">ClassInfo</span> <span class="variable">classInfo</span> <span class="operator">=</span> getClassFileFromJavaSource(javaCode);</span><br><span class="line">        List&lt;JavaFileObject&gt; javaFileObjects = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        javaFileObjects.add(<span class="keyword">new</span> <span class="title class_">StringJavaFileObject</span>(classInfo.className, javaCode));</span><br><span class="line"></span><br><span class="line">        <span class="comment">//使用编译选项可以改变默认编译行为。编译选项是一个元素为String类型的Iterable集合</span></span><br><span class="line">        List&lt;String&gt; options = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        options.add(<span class="string">&quot;-encoding&quot;</span>);</span><br><span class="line">        options.add(<span class="string">&quot;UTF-8&quot;</span>);</span><br><span class="line">        options.add(<span class="string">&quot;-classpath&quot;</span>);</span><br><span class="line">        options.add(classpath);</span><br><span class="line"></span><br><span class="line">        <span class="type">StringWriter</span> <span class="variable">outWriter</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StringWriter</span>();</span><br><span class="line">        JavaCompiler.<span class="type">CompilationTask</span> <span class="variable">task</span> <span class="operator">=</span> compiler.getTask(outWriter, fileManager, diagnostics, options, <span class="literal">null</span>, javaFileObjects);</span><br><span class="line">        <span class="comment">// 编译源程序</span></span><br><span class="line">        <span class="type">boolean</span> <span class="variable">success</span> <span class="operator">=</span> task.call();</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>整段代码还是比较简单的</p><ol><li>获取系统Java编译器</li><li>获取源码的类信息，比如名称，方法等等</li><li>将源码存储进<code>StringJavaFileObject</code></li><li>设置cp等进行编译</li></ol><p>大体的流程就是这几步就完成了。</p><p>整个过程中的难点是如果我们的工程是基于springboot的话，那么需要遍历springboot里面的文件夹和文件，针对springboot的处理可以参考 SpringBoot Loader 浅析</p><p>下面主要是说一下 对JavaFileManager的处理。在SpringBootLauncher里只是实现了对springboot fat jar的处理，但是具体和JavaCompiler 的融合还是在 SpringBootJavaFileManager 这个里处理的</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SpringBootJavaFileManager</span> <span class="keyword">extends</span> <span class="title class_">JrcJavaFileManager</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">Logger</span> <span class="variable">logger</span> <span class="operator">=</span> LoggerFactory.getLogger(SpringBootJavaFileManager.class);</span><br><span class="line"></span><br><span class="line">    SpringBootLauncher springBootLauncher;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">SpringBootJavaFileManager</span><span class="params">(StandardJavaFileManager standardManager)</span> &#123;</span><br><span class="line">        <span class="built_in">super</span>(standardManager);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            springBootLauncher = <span class="keyword">new</span> <span class="title class_">SpringBootLauncher</span>();</span><br><span class="line">            springBootLauncher.launch();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            logger.error(<span class="string">&quot;&quot;</span>, e);</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> ClassLoader <span class="title function_">getClassLoader</span><span class="params">(Location location)</span> &#123;</span><br><span class="line">        <span class="type">ClassLoader</span> <span class="variable">cl</span> <span class="operator">=</span> Thread.currentThread().getContextClassLoader();</span><br><span class="line"></span><br><span class="line">        ClassLoaderUtil.setClassLoader(<span class="keyword">new</span> <span class="title class_">JrcLaunchedURLClassLoader</span>(cl));</span><br><span class="line">        <span class="keyword">return</span> cl;</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> Iterable&lt;JavaFileObject&gt; <span class="title function_">list</span><span class="params">(Location location, String packageName, Set set, <span class="type">boolean</span> recurse)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">String</span> <span class="variable">packagePath</span> <span class="operator">=</span> packageName.replaceAll(<span class="string">&quot;\\.&quot;</span>, <span class="string">&quot;/&quot;</span>);</span><br><span class="line">        List&lt;SpringBootArchiveEntry&gt; entries = springBootLauncher.getEntries(packagePath);</span><br><span class="line"></span><br><span class="line">        List&lt;JavaFileObject&gt; list = entries.stream().map(it -&gt; <span class="keyword">new</span> <span class="title class_">JarJavaFileObject</span>(it, JavaFileObject.Kind.CLASS)).collect(Collectors.toList());</span><br><span class="line"></span><br><span class="line">        Iterable&lt;JavaFileObject&gt; superList = <span class="built_in">super</span>.list(location, packageName, set, recurse);</span><br><span class="line">        <span class="keyword">if</span> (superList == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> list;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (JavaFileObject o : superList) &#123;</span><br><span class="line">            list.add(o);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> list;</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">     * 将 JavaFileObject 转换成className</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> location PLATFORM_CLASS_PATH</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> file     /Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Comparable.class)</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> java.lang.Comparable</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> String <span class="title function_">inferBinaryName</span><span class="params">(Location location, JavaFileObject file)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (file <span class="keyword">instanceof</span> JarJavaFileObject) &#123;</span><br><span class="line">            <span class="keyword">return</span> file.getName();</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="built_in">super</span>.inferBinaryName(location, file);</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><ol><li>在构造<code>SpringBootJavaFileManager</code>实例的时候，开启SpringBoot fat jar的扫描。</li><li>重写 <code>getClassLoader()</code> 获取springboot loader里的 <code>LaunchedURLClassLoader</code> ，并将它设置成全局的classloader，主要是后面在执行方法时使用该classloader加载类</li><li>重写<code>list()</code> 方法，利用<code>SpringBootLauncher</code> 找到springboot fat jar里面的文件和文件夹</li><li>重写<code>inferBinaryName()</code> 方法，这是因为在list()方法中返回的是自定义的<code>JarJavaFileObject</code>，而<code>super.inferBinaryName()</code> 里有个校验，file 必须是 <code>BaseFileObject</code>,因此这里有个判断，如果是<code>JarJavaFileObject类型</code>，直接获取名字返回</li></ol><p>还有一点是对于classloader的处理，因为在执行方法的时候需要将编译的class字节码加载进jvm里，所以自定义了一个classloader</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">JrcLaunchedURLClassLoader</span> <span class="keyword">implements</span> <span class="title class_">JrcClassLoader</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">Logger</span> <span class="variable">logger</span> <span class="operator">=</span> LoggerFactory.getLogger(JrcLaunchedURLClassLoader.class);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> ClassLoader launchedURLClassLoader;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">JrcLaunchedURLClassLoader</span><span class="params">(ClassLoader launchedURLClassLoader)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.launchedURLClassLoader = launchedURLClassLoader;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Class <span class="title function_">defineClass</span><span class="params">(String name, <span class="type">byte</span>[] b)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> launchedURLClassLoader.loadClass(name);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (ClassNotFoundException e) &#123;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">Method</span> <span class="variable">defineClassMethod</span> <span class="operator">=</span> ClassLoader.class.getDeclaredMethod(<span class="string">&quot;defineClass&quot;</span>, <span class="keyword">new</span> <span class="title class_">Class</span>[]&#123;String.class, <span class="type">byte</span>[].class, <span class="type">int</span>.class, <span class="type">int</span>.class&#125;);</span><br><span class="line">            <span class="type">boolean</span> <span class="variable">isAccessible</span> <span class="operator">=</span> defineClassMethod.isAccessible();</span><br><span class="line">            <span class="keyword">if</span> (!isAccessible) &#123;</span><br><span class="line">                defineClassMethod.setAccessible(<span class="literal">true</span>);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="type">Object</span> <span class="variable">result</span> <span class="operator">=</span> defineClassMethod.invoke(launchedURLClassLoader, name, b, <span class="number">0</span>, b.length);</span><br><span class="line">            defineClassMethod.setAccessible(isAccessible);</span><br><span class="line"></span><br><span class="line">            <span class="keyword">return</span> (Class) result;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (NoSuchMethodException e) &#123;</span><br><span class="line">            logger.error(<span class="string">&quot;defineClass name:&#123;&#125;&quot;</span>, name, e);</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IllegalAccessException e) &#123;</span><br><span class="line">            logger.error(<span class="string">&quot;defineClass name:&#123;&#125;&quot;</span>, name, e);</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InvocationTargetException e) &#123;</span><br><span class="line">            logger.error(<span class="string">&quot;defineClass name:&#123;&#125;&quot;</span>, name, e);</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>大体的思路就是这样，具体的细节可以参考 jrc</p><p>PS: 当然大家也可以选择不上传java代码，直接将本地编译好的class字节码上传就可以了，这里也就是给大家提供一个思路。</p><p>另外更加产品化的东西可以考虑接入maven api实现依赖包的搜索下载，目前只能提供手动jar包上传方式。 ——-》 这个已经实现了</p>]]></content>
    
    
      
      
    <summary type="html">&lt;script src=&quot;/assets/asciinema-player.js&quot;&gt;&lt;/script&gt;&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;/assets/asciinema-player.css&quot; /&gt;
&lt;blockquote</summary>
      
    
    
    
    <category term="开源" scheme="https://wangmingco.github.io/categories/%E5%BC%80%E6%BA%90/"/>
    
    
  </entry>
  
  <entry>
    <title>基于SpringBoot/Vue/ElementUI 构建权限系统</title>
    <link href="https://wangmingco.github.io/2020/04/14/%E5%BC%80%E6%BA%90/%E5%9F%BA%E4%BA%8E%20SpringBoot%20Vue%20ElementUI%20%E6%9E%84%E5%BB%BA%E6%9D%83%E9%99%90%E7%B3%BB%E7%BB%9F/"/>
    <id>https://wangmingco.github.io/2020/04/14/%E5%BC%80%E6%BA%90/%E5%9F%BA%E4%BA%8E%20SpringBoot%20Vue%20ElementUI%20%E6%9E%84%E5%BB%BA%E6%9D%83%E9%99%90%E7%B3%BB%E7%BB%9F/</id>
    <published>2020-04-14T00:00:00.000Z</published>
    <updated>2025-12-19T01:50:58.670Z</updated>
    
    <content type="html"><![CDATA[<script src="/assets/asciinema-player.js"></script><link rel="stylesheet" type="text/css" href="/assets/asciinema-player.css" /><blockquote><p><a href="https://zhuanlan.zhihu.com/p/130412007">基于 SpringBoot/Vue/ElementUI 构建权限系统</a></p></blockquote><p>记录一下前几天写的一个权限系统。</p><div id="dplayer12" class="dplayer hexo-tag-dplayer-mark" style="margin-bottom: 20px;"></div><script>(function(){var player = new DPlayer({"container":document.getElementById("dplayer12"),"theme":"#FADFA3","video":{"url":"https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/videos/admin.mp4"},"danmaku":{"api":"https://api.prprpr.me/dplayer/"}});window.dplayers||(window.dplayers=[]);window.dplayers.push(player);})()</script> <p>整个系统基于RBAC模型构建，提供了前端权限控制(动态路由生成)和后端权限控制(接口访问权限)，其实还应该做一层数据访问权限，但是这和具体的业务结合比较紧凑，因此就没实现。</p><ol><li><code>User</code> 表存储用户登录信息</li><li><code>Role</code> 表存储角色</li><li><code>UserRoleRelation</code> 表存储用户角色关系</li><li><code>BackendPermission</code> 表存储后端权限(当服务器启动时会将所有路径都自动保存在该表里)</li><li><code>RoleBackendPermissionRelation</code> 表存储角色拥有的后端权限</li><li><code>FrontendPermission</code> 表存储前端路由信息.</li><li><code>RoleFrontendPermissionRelation</code> 表存储角色拥有的前端路由信息</li></ol><h2 id="后端系统"><a href="#后端系统" class="headerlink" title="后端系统"></a>后端系统</h2><p>后端系统是基于Springboot+shiro构建的，整个系统的核心就在shiro的配置上</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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></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_">ShiroConfig</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">Logger</span> <span class="variable">LOGGER</span> <span class="operator">=</span> LoggerFactory.getSystemLogger(ShiroConfig.class);</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> ApplicationContext applicationContext;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Resource</span></span><br><span class="line">    <span class="keyword">private</span> SecurityManager securityManager;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@PostConstruct</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">initStaticSecurityManager</span><span class="params">()</span> &#123;</span><br><span class="line">        SecurityUtils.setSecurityManager(securityManager);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean(name = &quot;securityManager&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> DefaultWebSecurityManager <span class="title function_">defaultWebSecurityManager</span><span class="params">(<span class="meta">@Autowired</span> DatabaseRealm shiroDatabaseRealm)</span> &#123;</span><br><span class="line">        <span class="type">DefaultWebSecurityManager</span> <span class="variable">securityManager</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DefaultWebSecurityManager</span>();</span><br><span class="line">        securityManager.setRealm(shiroDatabaseRealm);</span><br><span class="line">        securityManager.setSessionManager(buildSessionManager());</span><br><span class="line">        <span class="keyword">return</span> securityManager;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> SessionManager <span class="title function_">buildSessionManager</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">DefaultWebSessionManager</span> <span class="variable">sessionManager</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DefaultWebSessionManager</span>();</span><br><span class="line">        sessionManager.setSessionIdCookie(buildCookie());</span><br><span class="line">        sessionManager.setSessionIdCookieEnabled(<span class="literal">true</span>);</span><br><span class="line">        sessionManager.setSessionIdUrlRewritingEnabled(<span class="literal">false</span>);</span><br><span class="line">        <span class="comment">// 全局会话超时时间（单位毫秒），默认30分钟</span></span><br><span class="line">        sessionManager.setGlobalSessionTimeout(AuthConstant.GlobalSessionTimeout);</span><br><span class="line">        <span class="comment">// 是否开启删除无效的session对象  默认为true</span></span><br><span class="line">        sessionManager.setDeleteInvalidSessions(<span class="literal">true</span>);</span><br><span class="line">        <span class="comment">// 是否开启定时调度器进行检测过期session 默认为true</span></span><br><span class="line">        sessionManager.setSessionValidationSchedulerEnabled(<span class="literal">true</span>);</span><br><span class="line">        <span class="comment">// 设置session失效的扫描时间, 清理用户直接关闭浏览器造成的孤立会话 默认30分钟</span></span><br><span class="line">        sessionManager.setSessionValidationInterval(AuthConstant.SessionValidationInterval);</span><br><span class="line">        <span class="keyword">return</span> sessionManager;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> SimpleCookie <span class="title function_">buildCookie</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">SimpleCookie</span> <span class="variable">simpleCookie</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SimpleCookie</span>(TOKEN_NAME);</span><br><span class="line">        simpleCookie.setPath(<span class="string">&quot;/&quot;</span>);</span><br><span class="line">        <span class="comment">// 对服务器生成的TOKEN设置 HttpOnly 属性. 前端无法读写该TOKEN, 提供系统安全, 防止XSS攻击</span></span><br><span class="line">        simpleCookie.setHttpOnly(<span class="literal">true</span>);</span><br><span class="line">        <span class="comment">// 设置浏览器关闭时失效此Cookie</span></span><br><span class="line">        simpleCookie.setMaxAge(-<span class="number">1</span>);</span><br><span class="line">        <span class="keyword">return</span> simpleCookie;</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">     * 设置接口权限验证, 目前只针对api接口进行权限验证</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> securityManager</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">@Bean(name = &quot;shiroFilter&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> ShiroFilterFactoryBean <span class="title function_">shiroFilter</span><span class="params">(SecurityManager securityManager)</span> &#123;</span><br><span class="line">        LOGGER.info(<span class="string">&quot;start shiroFilter setting&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="type">ShiroFilterFactoryBean</span> <span class="variable">shiroFilterFactoryBean</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ShiroFilterFactoryBean</span>();</span><br><span class="line">        shiroFilterFactoryBean.setSecurityManager(securityManager);</span><br><span class="line"></span><br><span class="line">        shiroFilterFactoryBean.setLoginUrl(<span class="string">&quot;/&quot;</span>);</span><br><span class="line">        shiroFilterFactoryBean.setSuccessUrl(<span class="string">&quot;/#/dashboard&quot;</span>);</span><br><span class="line">        shiroFilterFactoryBean.setUnauthorizedUrl(<span class="string">&quot;/403&quot;</span>);</span><br><span class="line"></span><br><span class="line">        Map&lt;String, String&gt; filterChainDefinitionMap = <span class="keyword">new</span> <span class="title class_">LinkedHashMap</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">        Map&lt;String, Filter&gt; filtersMap = <span class="keyword">new</span> <span class="title class_">LinkedHashMap</span>&lt;&gt;();</span><br><span class="line">        filtersMap.put(<span class="string">&quot;apiAccessControlFilter&quot;</span>, <span class="keyword">new</span> <span class="title class_">ApiAccessControlFilter</span>());</span><br><span class="line">        shiroFilterFactoryBean.setFilters(filtersMap);</span><br><span class="line"></span><br><span class="line">        filterChainDefinitionMap.put(<span class="string">&quot;/static/**&quot;</span>, <span class="string">&quot;anon&quot;</span>);</span><br><span class="line">        filterChainDefinitionMap.put(<span class="string">&quot;/#/login/**&quot;</span>, <span class="string">&quot;anon&quot;</span>);</span><br><span class="line">        filterChainDefinitionMap.put(<span class="string">&quot;/api/user/auth/login&quot;</span>, <span class="string">&quot;anon&quot;</span>);</span><br><span class="line">        filterChainDefinitionMap.put(<span class="string">&quot;/logout&quot;</span>, <span class="string">&quot;logout&quot;</span>);</span><br><span class="line">        filterChainDefinitionMap.put(<span class="string">&quot;/api/**&quot;</span>, <span class="string">&quot;apiAccessControlFilter&quot;</span>);</span><br><span class="line">        filterChainDefinitionMap.put(<span class="string">&quot;/**&quot;</span>, <span class="string">&quot;logFilter&quot;</span>);</span><br><span class="line">        filterChainDefinitionMap.put(<span class="string">&quot;/**&quot;</span>, <span class="string">&quot;authc&quot;</span>);</span><br><span class="line"></span><br><span class="line">        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);</span><br><span class="line"></span><br><span class="line">        LOGGER.info(<span class="string">&quot;shirFilter config fineshed&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> shiroFilterFactoryBean;</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> CorsFilter <span class="title function_">corsFilter</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// CORS配置信息</span></span><br><span class="line">        <span class="type">CorsConfiguration</span> <span class="variable">config</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CorsConfiguration</span>();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (!SpringUtil.isInProduction(applicationContext)) &#123;</span><br><span class="line">            LOGGER.info(<span class="string">&quot;进行非生产模式CORS配置&quot;</span>);</span><br><span class="line">            config.addAllowedOrigin(<span class="string">&quot;*&quot;</span>);</span><br><span class="line">            config.setAllowCredentials(<span class="literal">true</span>);</span><br><span class="line">            config.addAllowedMethod(<span class="string">&quot;*&quot;</span>);</span><br><span class="line">            config.addAllowedHeader(<span class="string">&quot;*&quot;</span>);</span><br><span class="line">            config.addExposedHeader(<span class="string">&quot;Set-Cookie&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="type">UrlBasedCorsConfigurationSource</span> <span class="variable">configSource</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">UrlBasedCorsConfigurationSource</span>();</span><br><span class="line">        configSource.registerCorsConfiguration(<span class="string">&quot;/**&quot;</span>, config);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">CorsFilter</span>(configSource);</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></pre></td><td class="code"><pre><span class="line"><span class="type">SimpleCookie</span> <span class="variable">simpleCookie</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SimpleCookie</span>(TOKEN_NAME);</span><br><span class="line">simpleCookie.setHttpOnly(<span class="literal">true</span>);</span><br></pre></td></tr></table></figure><p>token是存储在cookie中，由后端传给前端的。而且这个cookie前端是不可读的，避免xss攻击。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><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">Map&lt;String, Filter&gt; filtersMap = <span class="keyword">new</span> <span class="title class_">LinkedHashMap</span>&lt;&gt;();</span><br><span class="line">filtersMap.put(<span class="string">&quot;apiAccessControlFilter&quot;</span>, <span class="keyword">new</span> <span class="title class_">ApiAccessControlFilter</span>());</span><br><span class="line">filterChainDefinitionMap.put(<span class="string">&quot;/api/user/auth/login&quot;</span>, <span class="string">&quot;anon&quot;</span>);</span><br><span class="line">filterChainDefinitionMap.put(<span class="string">&quot;/api/**&quot;</span>, <span class="string">&quot;apiAccessControlFilter&quot;</span>);</span><br></pre></td></tr></table></figure><p>定义了一个<code>ApiAccessControlFilter</code>，只有当访问以<code>/api/</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">if</span> (!SpringUtil.isInProduction(applicationContext)) &#123;</span><br><span class="line">            LOGGER.info(<span class="string">&quot;进行非生产模式CORS配置&quot;</span>);</span><br><span class="line">            config.addAllowedOrigin(<span class="string">&quot;*&quot;</span>);</span><br><span class="line">            config.setAllowCredentials(<span class="literal">true</span>);</span><br><span class="line">            config.addAllowedMethod(<span class="string">&quot;*&quot;</span>);</span><br><span class="line">            config.addAllowedHeader(<span class="string">&quot;*&quot;</span>);</span><br><span class="line">            config.addExposedHeader(<span class="string">&quot;Set-Cookie&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里做了一个是否在生产环境中的判断，因为在开发模式中，前端工程是直接运行在node服务中的，因此要做跨域访问，所以在非生产环境中允许跨域访问。</p><p>后端还有一些其他的功能，比如日志记录，参数校验，请求统计等等，这些非核心功能可以参考最后的工程代码。</p><h2 id="前端系统"><a href="#前端系统" class="headerlink" title="前端系统"></a>前端系统</h2><p>前端系统是基于vue-element-admin 进行二次开发的。</p><p>主要修改的就是cookie的存储，vue-element-admin 默认是通过http response body获取token，但是我修改成了通过header cookie返回，并且cookie默认不可读。</p><p>vue-element-admin 自带</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 登录  </span></span><br><span class="line"><span class="title function_">login</span>(<span class="params">&#123; commit &#125;, userInfo</span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123; username, password &#125; = userInfo</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="title function_">login</span>(&#123; <span class="attr">username</span>: username.<span class="title function_">trim</span>(), <span class="attr">password</span>: password &#125;).<span class="title function_">then</span>(<span class="function"><span class="params">response</span> =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">const</span> &#123; data &#125; = response</span><br><span class="line">        <span class="title function_">commit</span>(<span class="string">&#x27;SET_TOKEN&#x27;</span>, data.<span class="property">token</span>)</span><br><span class="line">        <span class="title function_">setToken</span>(data.<span class="property">token</span>)</span><br><span class="line">        <span class="title function_">resolve</span>()</span><br><span class="line">      &#125;).<span class="title function_">catch</span>(<span class="function"><span class="params">error</span> =&gt;</span> &#123;</span><br><span class="line">        <span class="title function_">reject</span>(error)</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">// 通信前获取token</span></span><br><span class="line">service.<span class="property">interceptors</span>.<span class="property">request</span>.<span class="title function_">use</span>(</span><br><span class="line">  <span class="function"><span class="params">config</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// do something before request is sent</span></span><br><span class="line">    <span class="keyword">return</span> config</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="function"><span class="params">error</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// do something with request error</span></span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(error) <span class="comment">// for debug</span></span><br><span class="line">    <span class="keyword">return</span> <span class="title class_">Promise</span>.<span class="title function_">reject</span>(error)</span><br><span class="line">  &#125;</span><br><span class="line">)</span><br></pre></td></tr></table></figure><p>还有一点修改就是前端路由的修改。</p><p>vue-element-admin 默认的是权限控制是在前端代码中控制的，后端只需要返回用户拥有的角色，然后前端根据角色信息找到权限然后生成路由。我修改成了整个前端的路由都是由后端控制返回的</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> actions = &#123;</span><br><span class="line">  <span class="title function_">generateRoutes</span>(<span class="params">&#123; commit &#125;, roles</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function"><span class="params">resolve</span> =&gt;</span> &#123;</span><br><span class="line">      <span class="title function_">getUserFrontendPermissions</span>().<span class="title function_">then</span>(<span class="function"><span class="params">response</span> =&gt;</span> &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">let</span> routeNodes = response.<span class="property">data</span>.<span class="property">routeNodes</span></span><br><span class="line">        <span class="title function_">importComponent</span>(routeNodes)</span><br><span class="line">        </span><br><span class="line">        <span class="title function_">commit</span>(<span class="string">&#x27;SET_ROUTES&#x27;</span>, routeNodes)</span><br><span class="line">        <span class="title function_">resolve</span>(routeNodes)</span><br><span class="line">      &#125;)</span><br><span class="line">      </span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">importComponent</span>(<span class="params">routeNodes</span>) &#123;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">for</span>(<span class="keyword">var</span> rn <span class="keyword">of</span> routeNodes) &#123;</span><br><span class="line">    <span class="keyword">if</span>(rn.<span class="property">component</span> == <span class="string">&quot;Layout&quot;</span>) &#123;</span><br><span class="line">      rn.<span class="property">component</span> = <span class="title class_">Layout</span></span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="keyword">let</span> componentPath = rn.<span class="property">component</span></span><br><span class="line">      rn.<span class="property">component</span> = <span class="function">() =&gt;</span> <span class="keyword">import</span>(<span class="string">`@/views/<span class="subst">$&#123;componentPath&#125;</span>`</span>)</span><br><span class="line">    &#125;</span><br><span class="line">   </span><br><span class="line">    <span class="keyword">if</span>(rn.<span class="property">children</span> &amp;&amp; rn.<span class="property">children</span>.<span class="property">length</span> &gt; <span class="number">0</span>) &#123;</span><br><span class="line">      <span class="title function_">importComponent</span>(rn.<span class="property">children</span>)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>主要的函数就是importComponent(routeNodes), 采用递归的方式import组件.</p><blockquote><p>这里遇到一点小问题，webpack 编译es6 动态引入 import() 时不能传入变量, 但一定要用变量的时候，可以通过字符串模板来提供部分信息给webpack；例如import(./path/${myFile}), 这样编译时会编译所有./path下的模块. 参考在vue中import()语法为什么不能传入变量?</p></blockquote><p>整体的代码就是这么多吧，当然在整个调试过程中还是花了些时间的，具体的可以参考我的提交记录 admin-solution</p>]]></content>
    
    
      
      
    <summary type="html">&lt;script src=&quot;/assets/asciinema-player.js&quot;&gt;&lt;/script&gt;&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;/assets/asciinema-player.css&quot; /&gt;
&lt;blockquote</summary>
      
    
    
    
    <category term="开源" scheme="https://wangmingco.github.io/categories/%E5%BC%80%E6%BA%90/"/>
    
    
  </entry>
  
  <entry>
    <title>当 Netty 遇上 Spring Boot</title>
    <link href="https://wangmingco.github.io/2019/12/09/%E5%BC%80%E6%BA%90/%E5%BD%93%20Netty%20%E9%81%87%E4%B8%8A%20Spring%20Boot/"/>
    <id>https://wangmingco.github.io/2019/12/09/%E5%BC%80%E6%BA%90/%E5%BD%93%20Netty%20%E9%81%87%E4%B8%8A%20Spring%20Boot/</id>
    <published>2019-12-09T00:00:00.000Z</published>
    <updated>2025-12-19T01:50:58.670Z</updated>
    
    <content type="html"><![CDATA[<script src="/assets/asciinema-player.js"></script><link rel="stylesheet" type="text/css" href="/assets/asciinema-player.css" /><blockquote><p><a href="https://zhuanlan.zhihu.com/p/96378920">当 Netty 遇上 Spring Boot</a> 本文是最基于项目开始时写的，后期代码重构过几次，但是思路是一致的</p></blockquote><p>当Netty遇上Spring Boot会发生什么呢? 当然是 <a href="https://github.com/wangmingco/netty-spring-boot-starter">netty-spring-boot-starter</a> 啦</p><p>周末有些闲暇时间, 便想将Netty与Spring Boot整合到一起, 看到了各种starter, 那干脆整一个 netty-spring-boot-starter 吧.<br>netty-spring-boot-starter</p><div id="dplayer13" class="dplayer hexo-tag-dplayer-mark" style="margin-bottom: 20px;"></div><script>(function(){var player = new DPlayer({"container":document.getElementById("dplayer13"),"theme":"#FADFA3","video":{"url":"https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/videos/nsb.mp4"},"danmaku":{"api":"https://api.prprpr.me/dplayer/"}});window.dplayers||(window.dplayers=[]);window.dplayers.push(player);})()</script> <p>第一版要实现如下功能</p><ol><li>通过<code>@SpringBootApplication</code> 注解启动服务后, Netty服务能够启动起来</li><li>能够识别到类似于<code>GetMapping</code>注解的自定义消息处理注解<code>CommandController</code>和<code>CommandMapping</code></li><li>Netty收到消息后能够转发到Spring上下文中进行方法调用</li></ol><p>嗯, 就是这些….</p><p>根据王总的说法, 我们的小目标建立好了, 接下来咋整呢?</p><p>首先呢, 当然是从网上找个教程啦, 看看starter是咋弄的, google一下找到第一篇文章, 运用Ctrl C/Ctrl V大法, 欧了, 服务起来了, 当然还是得看看大象装冰箱, 总共分几步</p><h4 id="1-添加依赖"><a href="#1-添加依赖" class="headerlink" title="1. 添加依赖"></a>1. 添加依赖</h4><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></pre></td><td class="code"><pre><span class="line">            <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-autoconfigure<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>$&#123;spring.boot.version&#125;<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>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-configuration-processor<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">version</span>&gt;</span>$&#123;spring.boot.version&#125;<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">optional</span>&gt;</span>true<span class="tag">&lt;/<span class="name">optional</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">groupId</span>&gt;</span>io.netty<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>netty-all<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>$&#123;netty.version&#125;<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><h4 id="2-写一下配置类-用于自定义application-properties里的参数"><a href="#2-写一下配置类-用于自定义application-properties里的参数" class="headerlink" title="2. 写一下配置类, 用于自定义application.properties里的参数"></a>2. 写一下配置类, 用于自定义<code>application.properties</code>里的参数</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@ConfigurationProperties(prefix = &quot;spring.boot.netty&quot;)</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SpringBootNettyProperties</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="type">Integer</span> <span class="variable">PORT</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="3-再把配置类整上-在这里启动Netty服务器"><a href="#3-再把配置类整上-在这里启动Netty服务器" class="headerlink" title="3. 再把配置类整上, 在这里启动Netty服务器"></a>3. 再把配置类整上, 在这里启动Netty服务器</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@EnableConfigurationProperties(SpringBootNettyProperties.class)</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SpringBootNettyConfiguration</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> SpringBootNettyProperties springBootNettyProperties;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="meta">@ConditionalOnMissingBean(NettyStarter.class)</span></span><br><span class="line">    <span class="keyword">public</span> NettyStarter <span class="title function_">nettyStarter</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_">NettyStarter</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Component</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">NettyStarter</span> <span class="keyword">implements</span> <span class="title class_">InitializingBean</span>, DisposableBean &#123;</span><br><span class="line">        <span class="meta">@Resource</span></span><br><span class="line">        <span class="keyword">private</span> SpringBootNettyProperties springBootNettyProperties;</span><br><span class="line">        <span class="keyword">private</span> NettyServer nettyServer;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">afterPropertiesSet</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">            <span class="type">NettyServer</span> <span class="variable">nettyServer</span> <span class="operator">=</span> NettyServer.builder().build();</span><br><span class="line">            nettyServer.start();</span><br><span class="line">            <span class="built_in">this</span>.nettyServer = nettyServer;</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_">destroy</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">            nettyServer.stop();</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>这里我写了一个内部类NettyStarter, 作为Netty服务启动的入口. 并且把它注册为一个Bean, 因为在客户端程序中, 可能是扫描不到我这个包的, 所以在配置类中, 直接把它注册到容器里.</p><h4 id="4-实现自定义注解扫描"><a href="#4-实现自定义注解扫描" class="headerlink" title="4. 实现自定义注解扫描"></a>4. 实现自定义注解扫描</h4><p>我自己写了俩个注解</p><ul><li><code>CommandController</code> 类似于<code>RestController</code>, 注解在类上, 表示这个类是用来处理Netty消息的</li><li><code>CommandMapping</code> 类似于<code>GetMapping</code>, 注解在方法上, 表示这个方法是用来接收Netty消息的</li></ul><p>然后实现Spring为我们提供的<code>ResourceLoaderAware</code>, <code>ImportBeanDefinitionRegistrar</code>接口</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CommandScannerRegistrar</span> <span class="keyword">implements</span> <span class="title class_">ResourceLoaderAware</span>, ImportBeanDefinitionRegistrar &#123;</span><br><span class="line">    <span class="keyword">private</span> ResourceLoader resourceLoader;</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_">registerBeanDefinitions</span><span class="params">(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry)</span> &#123;</span><br><span class="line">       String[] basePackages = annoAttrs.getStringArray(<span class="string">&quot;basePackage&quot;</span>);</span><br><span class="line">        <span class="comment">//自定义的包扫描器</span></span><br><span class="line">        <span class="type">CommandClassPathScanner</span> <span class="variable">commandClassPathScanner</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CommandClassPathScanner</span>(beanDefinitionRegistry, <span class="literal">false</span>);</span><br><span class="line">        <span class="comment">//扫描指定路径下的接口</span></span><br><span class="line">        Set&lt;BeanDefinitionHolder&gt; beanDefinitionHolders = commandClassPathScanner.doScan(basePackages);</span><br><span class="line">        registerCommandMapping(beanDefinitionRegistry, beanDefinitionHolders);</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_">registerCommandMapping</span><span class="params">(BeanDefinitionRegistry beanDefinitionRegistry, Set&lt;BeanDefinitionHolder&gt; beanDefinitionHolders)</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">beanClassName</span> <span class="operator">=</span> beanDefinitionHolder.getBeanDefinition().getBeanClassName();</span><br><span class="line"></span><br><span class="line">            <span class="type">Class</span> <span class="variable">beanClass</span> <span class="operator">=</span> Class.forName(beanClassName);</span><br><span class="line">            <span class="keyword">for</span> (Method method : beanClass.getMethods()) &#123;</span><br><span class="line">                <span class="type">CommandMapping</span> <span class="variable">commandMappingAnnotation</span> <span class="operator">=</span> method.getAnnotation(CommandMapping.class);</span><br><span class="line">                List&lt;ParameterInfo&gt; parameterInfoList = handleParameter(method);</span><br><span class="line"></span><br><span class="line">                <span class="type">MethodInfo</span> <span class="variable">methodInfo</span> <span class="operator">=</span> MethodInfo.builder()</span><br><span class="line">                        .parameterInfoList(parameterInfoList)</span><br><span class="line">                        .targetMethod(method)</span><br><span class="line">                        .targetBeanClass(beanClass)</span><br><span class="line">                        .build();</span><br><span class="line"></span><br><span class="line">                CommandMethodCache.add(String.valueOf(commandMappingAnnotation.id()), methodInfo);</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">private</span> List&lt;ParameterInfo&gt; <span class="title function_">handleParameter</span><span class="params">(Method method)</span> &#123;</span><br><span class="line">        List&lt;ParameterInfo&gt; parameterInfoList = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        <span class="keyword">for</span> (Class parameterType : method.getParameterTypes()) &#123;</span><br><span class="line">            ParameterInfo.<span class="type">ParameterInfoBuilder</span> <span class="variable">parameterInfoBuilder</span> <span class="operator">=</span> ParameterInfo.builder();</span><br><span class="line">            <span class="keyword">if</span> (GeneratedMessageV3.class.isAssignableFrom(parameterType)) &#123;</span><br><span class="line">                setParser(parameterInfoBuilder, parameterType);</span><br><span class="line">            &#125;</span><br><span class="line">            parameterInfoList.add(parameterInfoBuilder.build());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> parameterInfoList;</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_">setParser</span><span class="params">(ParameterInfo.ParameterInfoBuilder builder, Class&lt;?&gt; parameterType)</span> &#123;</span><br><span class="line">        <span class="type">Field</span> <span class="variable">parserField</span> <span class="operator">=</span> parameterType.getDeclaredField(<span class="string">&quot;PARSER&quot;</span>);</span><br><span class="line">        parserField.setAccessible(<span class="literal">true</span>);</span><br><span class="line">        <span class="type">Parser</span> <span class="variable">parser</span> <span class="operator">=</span> (Parser) parserField.get(parameterType);</span><br><span class="line">        builder.parser(parser);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这段代码是一段比较核心的代码, 而且也比较长, 我一点一点来说.</p><p><code>ImportBeanDefinitionRegistrar</code> 这个接口是用来做动态注册bean的, 我在<code>registerBeanDefinitions</code> 方法中实现对自定义注解的扫描, 然后注册到<code>beanDefinitionRegistry</code>里.</p><p>对于自定义注解扫描的话, 我继承了<code>ClassPathBeanDefinitionScanner</code>类, 写了一个扫描器, 很简单, 可以直接看我项目里的源码.</p><p>写到这里的时候, 第一个难点出现了, Spring只扫描到了类也就是<code>CommandController</code>, 而没有扫描到<code>CommandMapping</code>, 当时有些心烦意乱就没有去看spring里对于<code>GetMapping</code>等注解的处理, 就直接用反射的方式简单处理了一下, 这点后期可以优化成利用动态代理或者ASM等框架实现, 提高一下性能.</p><p>这里的思路是我把<code>CommandController</code>类里的每个方法都遍历一遍, 如果有<code>CommandMapping</code>注解我就缓存起来, 然后看<code>CommandMapping</code>的方法里是否有Protobuf类, 如果有的话, 我就获取到它的Parser, 也缓存起来, 等到解析消息时候用.</p><p>到这里Spring Boot的处理基本上就完了, 接下来看看Netty里是咋实现的</p><h4 id="5-Netty相关"><a href="#5-Netty相关" class="headerlink" title="5. Netty相关"></a>5. Netty相关</h4><p>首先是启动Netty服务的代码, 很简单</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">NettyServer</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> EventLoopGroup bossGroup;</span><br><span class="line">    <span class="keyword">private</span> EventLoopGroup workerGroup;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">start</span><span class="params">()</span> &#123;</span><br><span class="line"></span><br><span class="line">        log.info(<span class="string">&quot;Netty Server starting...&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">EventLoopGroup</span> <span class="variable">bossGroup</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NioEventLoopGroup</span>(<span class="number">1</span>);</span><br><span class="line">            <span class="type">EventLoopGroup</span> <span class="variable">workerGroup</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NioEventLoopGroup</span>();</span><br><span class="line">            <span class="type">ServerBootstrap</span> <span class="variable">b</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ServerBootstrap</span>();</span><br><span class="line">            b.group(bossGroup, workerGroup)</span><br><span class="line">                    .channel(NioServerSocketChannel.class)</span><br><span class="line">                    .handler(<span class="keyword">new</span> <span class="title class_">LoggingHandler</span>(LogLevel.DEBUG))</span><br><span class="line">                    .childHandler(<span class="keyword">new</span> <span class="title class_">ChannelInitializer</span>&lt;SocketChannel&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_">initChannel</span><span class="params">(SocketChannel ch)</span> &#123;</span><br><span class="line">                            <span class="type">ChannelPipeline</span> <span class="variable">p</span> <span class="operator">=</span> ch.pipeline();</span><br><span class="line">                            p.addLast(<span class="keyword">new</span> <span class="title class_">NettyServerHandler</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="type">int</span> <span class="variable">port</span> <span class="operator">=</span> <span class="number">8081</span>;</span><br><span class="line">            <span class="keyword">if</span> (NettyConfig.getPORT() != <span class="literal">null</span>) &#123;</span><br><span class="line">                port = NettyConfig.getPORT().getValue();</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            b.bind(<span class="string">&quot;localhost&quot;</span>, port).sync();</span><br><span class="line"></span><br><span class="line">            log.info(<span class="string">&quot;Netty Server listening at:&#123;&#125;&quot;</span>, port);</span><br><span class="line"></span><br><span class="line">            <span class="built_in">this</span>.bossGroup = bossGroup;</span><br><span class="line">            <span class="built_in">this</span>.workerGroup = workerGroup;</span><br><span class="line"></span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">            log.error(<span class="string">&quot;&quot;</span>, e);</span><br><span class="line">            stop();</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">void</span> <span class="title function_">stop</span><span class="params">()</span> &#123;</span><br><span class="line">        bossGroup.shutdownGracefully();</span><br><span class="line">        workerGroup.shutdownGracefully();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里省略了使用<code>application.properties</code>配置Netty的部分, 有兴趣可以去看看源码. 而且目前也只是抽象出来<code>ChannelOption</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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">NettyServerHandler</span> <span class="keyword">extends</span> <span class="title class_">ByteToMessageDecoder</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">int</span> <span class="variable">MIN_PACKAGE_SIZE</span> <span class="operator">=</span> <span class="number">8</span>;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">decode</span><span class="params">(ChannelHandlerContext ctx, ByteBuf in, List out)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (in.readableBytes() &lt; MIN_PACKAGE_SIZE) &#123;</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">        in.markReaderIndex();</span><br><span class="line">        <span class="type">int</span> <span class="variable">messageSize</span> <span class="operator">=</span> in.readByte();</span><br><span class="line">        <span class="type">int</span> <span class="variable">type</span> <span class="operator">=</span> in.readByte();</span><br><span class="line">        <span class="type">int</span> <span class="variable">readableBytes</span> <span class="operator">=</span> in.readableBytes();</span><br><span class="line">        <span class="keyword">if</span> (readableBytes &lt; messageSize) &#123;</span><br><span class="line">            in.resetReaderIndex();</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="type">MessageType</span> <span class="variable">messageType</span> <span class="operator">=</span> MessageType.get(type);</span><br><span class="line">        <span class="type">ByteBuf</span> <span class="variable">messageByteBuf</span> <span class="operator">=</span> in.readBytes(messageSize);</span><br><span class="line">        <span class="type">byte</span>[] messageBytes = <span class="keyword">new</span> <span class="title class_">byte</span>[messageSize];</span><br><span class="line">        messageByteBuf.getBytes(<span class="number">0</span>, messageBytes);</span><br><span class="line">        CommandDispatcher.dispatch(ctx, messageType, messageBytes);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>整个思路也很简单就看当前可读byte是否够, 够的话就读取, 不够就返回, 等待byte攒够了再处理.</p><p>读取完message后就调用CommandDispatcher进行消息派发.</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><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></pre></td><td class="code"><pre><span class="line">public class CommandDispatcher &#123;</span><br><span class="line"></span><br><span class="line">    public static void dispatch(ChannelHandlerContext ctx, MessageType messageType, byte[] messageBytes) &#123;</span><br><span class="line">        MethodInfo methodInfo = CommandMethodCache.getMethodInfo(String.valueOf(messageType.getType()));</span><br><span class="line"></span><br><span class="line">        List&lt;ParameterInfo&gt; parameterInfoList = methodInfo.getParameterInfoList();</span><br><span class="line">        Class targetBeanClass = methodInfo.getTargetBeanClass();</span><br><span class="line">        Method targetMethod = methodInfo.getTargetMethod();</span><br><span class="line"></span><br><span class="line">        // 生成调用方法参数</span><br><span class="line">        List paramters = getParameters(ctx, messageType, messageBytes, parameterInfoList);</span><br><span class="line">        // 调用方法</span><br><span class="line">        Object result = invoke(targetBeanClass, targetMethod, paramters);</span><br><span class="line">        // 调用方法后可能产生应答, 将应答返回给前端</span><br><span class="line">        response(ctx, messageType, result);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    private static List getParameters(ChannelHandlerContext ctx, MessageType messageType, byte[] messageBytes, List&lt;ParameterInfo&gt; parameterInfoList) &#123;</span><br><span class="line">        List paramters = new ArrayList();</span><br><span class="line">        for (ParameterInfo parameterInfo : parameterInfoList) &#123;</span><br><span class="line">            switch (messageType) &#123;</span><br><span class="line">                case PROTOBUF: &#123;</span><br><span class="line">                    if (parameterInfo.getParser() != null) &#123;</span><br><span class="line">                        addProtobugParam(messageBytes, paramters, parameterInfo);</span><br><span class="line">                    &#125;</span><br><span class="line">                    break;</span><br><span class="line">                &#125;</span><br><span class="line">                default:</span><br><span class="line">                    throw new IllegalStateException(&quot;Unexpected value: &quot; + messageType);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        return paramters;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    private static void addProtobugParam(byte[] messageBytes, List paramters, ParameterInfo parameterInfo) &#123;</span><br><span class="line">        Parser parser = parameterInfo.getParser();</span><br><span class="line">        paramters.add(parser.parseFrom(messageBytes));</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    private static Object invoke(Class targetBeanClass, Method targetMethod, List paramters) &#123;</span><br><span class="line">        Object result = null;</span><br><span class="line">        Object methodBean = SpringContext.getBean(targetBeanClass);</span><br><span class="line">        if (paramters.size() &gt; 0) &#123;</span><br><span class="line">            Object[] params = paramters.toArray();</span><br><span class="line">            result = targetMethod.invoke(methodBean, params);</span><br><span class="line">        &#125; else &#123;</span><br><span class="line">            result = targetMethod.invoke(methodBean);</span><br><span class="line">        &#125;</span><br><span class="line">        return result;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    private static void response(ChannelHandlerContext ctx, MessageType messageType, Object result) &#123;</span><br><span class="line"></span><br><span class="line">        if (GeneratedMessageV3.class.isAssignableFrom(result.getClass())) &#123;</span><br><span class="line">            GeneratedMessageV3 generatedMessage = (GeneratedMessageV3) result;</span><br><span class="line">            byte[] bytearray = generatedMessage.toByteArray();</span><br><span class="line">            ByteBuf response = ByteBufAllocator.DEFAULT.heapBuffer(bytearray.length)</span><br><span class="line">                    .writeByte(bytearray.length)</span><br><span class="line">                    .writeByte(MessageType.PROTOBUF.getType())</span><br><span class="line">                    .writeBytes(bytearray);</span><br><span class="line">            ctx.writeAndFlush(response);</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>根据消息号拿到我们是上一步缓存的方法和Paser, 进行参数生成. </li><li>拿到生成好的参数之后, 进行方法调用</li><li>判断方法返回值是否需要回写到客户端, 目前是只有protobuf类型的返回值才会回写到客户端</li></ol><p>嗯, 到这里基本上整个逻辑就写完了, 说起来比较简单, 但是在方法转发这一块还是费了点力气 不过还好, 总算是周末的时光没有白过, 整完了.</p><p>整个工程还有一些待优化的点, 比如对于UDP的支持, 后期有时间把这里整理一下..<br>编辑于 2022-02-18 14:57<br>Java 框架<br>Netty<br>Spring Boot<br>评论千万条，友善第一条</p><p>17 条评论<br>默认<br>时间<br>暗香浮动月黄昏<br>暗香浮动月黄昏</p><p>新手提问，请问您用springboot集成的这个netty，可以用来做什么，或者说可以实现什么功能呢<br>2021-03-23<br>· 作者回复了<br>代号One<br>代号One<br>作者<br>比如说在推送服务中心使用<br>02-11<br>金浩彬<br>金浩彬</p><p>大佬求教<br>2021-02-25<br>· 作者回复了<br>代号One<br>代号One<br>作者<br>?<br>2021-08-26<br>maodun<br>maodun</p><p>netty做成starter感觉不太实用。<br>2020-08-04<br>非鬼亦非仙<br>非鬼亦非仙</p><p>有一个疑问，这样整合Netty服务的性能，是否受制于springboot内置的容器Tomcat，如果受制，把Netty整合到springboot启动中，是不是本末倒置？<br>2020-03-07<br>· 作者回复了<br>代号One<br>代号One<br>作者<br>非鬼亦非仙</p><p>springframework核心是个对象容器, 提供IOC, DI等特性, 然后在这个基础之上扩展了AOP, Web, Jpa等其他特性.</p><p>用springboot举个例子, 比如我们有一个很简单的springboot项目, 当运行 SpringApplication.run(XXX.class) 的时候SpringApplication内部会调用createApplicationContext(), 创建出ConfigurableApplicationContext(具体类型是AnnotationConfigServletWebServerApplicationContext). 然后继续调用AbstractApplicationContext的refresh()方法进行spring容器的初始化, 在初始化过程中就会进行IOD, AOP, Web等等功能的实现.</p><p>在AbstractApplicationContext#refresh()中调用onRefresh()方法, ServletWebServerApplicationContext对这个方法重写了, 会调用 createWebServer() , 在这个方法中创建一个Tomcat服务器.</p>]]></content>
    
    
      
      
    <summary type="html">&lt;script src=&quot;/assets/asciinema-player.js&quot;&gt;&lt;/script&gt;&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;/assets/asciinema-player.css&quot; /&gt;
&lt;blockquote</summary>
      
    
    
    
    <category term="开源" scheme="https://wangmingco.github.io/categories/%E5%BC%80%E6%BA%90/"/>
    
    
  </entry>
  
  <entry>
    <title>Java String intern() 实现细节</title>
    <link href="https://wangmingco.github.io/2019/07/03/Java/javase/Java%20String%20intern%20%E5%AE%9E%E7%8E%B0%E7%BB%86%E8%8A%82/"/>
    <id>https://wangmingco.github.io/2019/07/03/Java/javase/Java%20String%20intern%20%E5%AE%9E%E7%8E%B0%E7%BB%86%E8%8A%82/</id>
    <published>2019-07-03T00:00:00.000Z</published>
    <updated>2025-12-19T01:50:58.658Z</updated>
    
    <content type="html"><![CDATA[<script src="/assets/asciinema-player.js"></script><link rel="stylesheet" type="text/css" href="/assets/asciinema-player.css" /><blockquote><p><a href="https://zhuanlan.zhihu.com/p/72054668">Java String intern() 实现细节</a></p></blockquote><p>在Java String这个类中有个intern()方法, 该方法是用来将String内部的char数组缓存到JVM内部的字符串常量池中去, 使用方法及方法声明如下</p><figure class="highlight java"><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">String</span> <span class="variable">c</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">String</span>(<span class="string">&quot;123&quot;</span>).intern();</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">native</span> String <span class="title function_">intern</span><span class="params">()</span>;</span><br></pre></td></tr></table></figure><p>我们看到intern() 方法是个native方法.</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">// openjdk-jdk8u-jdk8u/jdk/src/share/native/java/lang/String.c</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;jvm.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;java_lang_String.h&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function">JNIEXPORT jobject JNICALL</span></span><br><span class="line"><span class="function"><span class="title">Java_java_lang_String_intern</span><span class="params">(JNIEnv *env, jobject <span class="keyword">this</span>)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">JVM_InternString</span>(env, <span class="keyword">this</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>Java_java_lang_String_intern</code> 实现非常简单, 就是直接调用 <code>JVM_InternString()</code> 方法</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">// openjdk-jdk8u-jdk8u/hotspot/src/share/vm/prims/jvm.cpp</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">JVM_ENTRY</span>(jstring, <span class="built_in">JVM_InternString</span>(JNIEnv *env, jstring str))</span><br><span class="line">  <span class="built_in">JVMWrapper</span>(<span class="string">&quot;JVM_InternString&quot;</span>);</span><br><span class="line">  JvmtiVMObjectAllocEventCollector oam;</span><br><span class="line">  <span class="keyword">if</span> (str == <span class="literal">NULL</span>) <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">  oop string = JNIHandles::<span class="built_in">resolve_non_null</span>(str);</span><br><span class="line">  oop result = StringTable::<span class="built_in">intern</span>(string, CHECK_NULL);</span><br><span class="line">  <span class="keyword">return</span> (jstring) JNIHandles::<span class="built_in">make_local</span>(env, result);</span><br><span class="line">JVM_END</span><br></pre></td></tr></table></figure><p>首先将<code>jstring</code>类型解析成oop类型的字符串, 接着调用<code>StringTable</code>的<code>intern()</code>.</p><p><code>StringTable</code>定义在了<code>symbolTable.hpp</code>文件中, 下来看一下intern()方法的实现</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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">// openjdk-jdk8u-jdk8u/hotspot/src/share/vm/classfile/symbolTable.cpp</span></span><br><span class="line"></span><br><span class="line"><span class="function">oop <span class="title">StringTable::intern</span><span class="params">(<span class="type">const</span> <span class="type">char</span>* utf8_string, TRAPS)</span> </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (utf8_string == <span class="literal">NULL</span>) <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">  <span class="function">ResourceMark <span class="title">rm</span><span class="params">(THREAD)</span></span>;</span><br><span class="line">  <span class="type">int</span> length = UTF8::<span class="built_in">unicode_length</span>(utf8_string);</span><br><span class="line">  jchar* chars = <span class="built_in">NEW_RESOURCE_ARRAY</span>(jchar, length);</span><br><span class="line">  UTF8::<span class="built_in">convert_to_unicode</span>(utf8_string, chars, length);</span><br><span class="line">  Handle string;</span><br><span class="line">  oop result = <span class="built_in">intern</span>(string, chars, length, CHECK_NULL);</span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在上一步<code>intern()</code>方法中, 首先将字符数组转化为unicode编码, 然后接着调用下面的intern()方法</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">oop <span class="title">StringTable::intern</span><span class="params">(Handle string_or_null, jchar* name,</span></span></span><br><span class="line"><span class="params"><span class="function">                        <span class="type">int</span> len, TRAPS)</span> </span>&#123;</span><br><span class="line">  <span class="type">unsigned</span> <span class="type">int</span> hashValue = <span class="built_in">hash_string</span>(name, len);</span><br><span class="line">  <span class="type">int</span> index = <span class="built_in">the_table</span>()-&gt;<span class="built_in">hash_to_index</span>(hashValue);</span><br><span class="line">  oop found_string = <span class="built_in">the_table</span>()-&gt;<span class="built_in">lookup</span>(index, name, len, hashValue);</span><br><span class="line"></span><br><span class="line">  <span class="comment">// Found</span></span><br><span class="line">  <span class="keyword">if</span> (found_string != <span class="literal">NULL</span>) &#123;</span><br><span class="line">    <span class="built_in">ensure_string_alive</span>(found_string);</span><br><span class="line">    <span class="keyword">return</span> found_string;</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><br><span class="line">  Handle string;</span><br><span class="line">  <span class="comment">// try to reuse the string if possible</span></span><br><span class="line">  <span class="keyword">if</span> (!string_or_null.<span class="built_in">is_null</span>()) &#123;</span><br><span class="line">    string = string_or_null;</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    string = java_lang_String::<span class="built_in">create_from_unicode</span>(name, len, CHECK_NULL);</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><br><span class="line">  <span class="comment">// Grab the StringTable_lock before getting the_table() because it could</span></span><br><span class="line">  <span class="comment">// change at safepoint.</span></span><br><span class="line">  oop added_or_found;</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="function">MutexLocker <span class="title">ml</span><span class="params">(StringTable_lock, THREAD)</span></span>;</span><br><span class="line">    <span class="comment">// Otherwise, add to symbol to table</span></span><br><span class="line">    added_or_found = <span class="built_in">the_table</span>()-&gt;<span class="built_in">basic_add</span>(index, string, name, len,</span><br><span class="line">                                  hashValue, CHECK_NULL);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="built_in">ensure_string_alive</span>(added_or_found);</span><br><span class="line">  <span class="keyword">return</span> added_or_found;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">static</span> StringTable* <span class="title">the_table</span><span class="params">()</span> </span>&#123; <span class="keyword">return</span> _the_table; &#125;</span><br><span class="line"></span><br><span class="line">StringTable* StringTable::_the_table = <span class="literal">NULL</span>;</span><br></pre></td></tr></table></figure><p>在上面的逻辑中我们可以看到, 首先是通过<code>the_table()</code>方法找到<code>StringTable</code>, 然后调用<code>lookup()</code>方法, 进行查找, 如果找到的话则直接返回找到的引用.如果找不到的话则调用<code>StringTable#basic_add()</code>方法将其添加. 添加完之后返回其引用地址.</p><p>StringTable的定义如下</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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="keyword">class</span> <span class="title class_">StringTable</span> : <span class="keyword">public</span> RehashableHashtable&lt;oop, mtSymbol&gt; &#123;</span><br><span class="line">  <span class="keyword">friend</span> <span class="keyword">class</span> <span class="title class_">VMStructs</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">  <span class="comment">// The string table</span></span><br><span class="line">  <span class="type">static</span> StringTable* _the_table;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="type">static</span> oop <span class="title">intern</span><span class="params">(Handle string_or_null, jchar* chars, <span class="type">int</span> length, TRAPS)</span></span>;</span><br><span class="line">  <span class="function">oop <span class="title">basic_add</span><span class="params">(<span class="type">int</span> index, Handle string_or_null, jchar* name, <span class="type">int</span> len,</span></span></span><br><span class="line"><span class="params"><span class="function">                <span class="type">unsigned</span> <span class="type">int</span> hashValue, TRAPS)</span></span>;</span><br><span class="line"></span><br><span class="line">  <span class="function">oop <span class="title">lookup</span><span class="params">(<span class="type">int</span> index, jchar* chars, <span class="type">int</span> length, <span class="type">unsigned</span> <span class="type">int</span> hashValue)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">  <span class="comment">// The string table</span></span><br><span class="line">  <span class="function"><span class="type">static</span> StringTable* <span class="title">the_table</span><span class="params">()</span> </span>&#123; <span class="keyword">return</span> _the_table; &#125;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="type">static</span> <span class="type">void</span> <span class="title">create_table</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="built_in">assert</span>(_the_table == <span class="literal">NULL</span>, <span class="string">&quot;One string table allowed.&quot;</span>);</span><br><span class="line">    _the_table = <span class="keyword">new</span> <span class="built_in">StringTable</span>();</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// GC support</span></span><br><span class="line">  <span class="comment">//   Delete pointers to otherwise-unreachable objects.</span></span><br><span class="line">  <span class="function"><span class="type">static</span> <span class="type">void</span> <span class="title">unlink</span><span class="params">(BoolObjectClosure* cl)</span> </span>&#123;</span><br><span class="line">    <span class="type">int</span> processed = <span class="number">0</span>;</span><br><span class="line">    <span class="type">int</span> removed = <span class="number">0</span>;</span><br><span class="line">    <span class="built_in">unlink_or_oops_do</span>(cl, <span class="literal">NULL</span>, &amp;processed, &amp;removed);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// Probing</span></span><br><span class="line">  <span class="function"><span class="type">static</span> oop <span class="title">lookup</span><span class="params">(Symbol* symbol)</span></span>;</span><br><span class="line">  <span class="function"><span class="type">static</span> oop <span class="title">lookup</span><span class="params">(jchar* chars, <span class="type">int</span> length)</span></span>;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// Interning</span></span><br><span class="line">  <span class="function"><span class="type">static</span> oop <span class="title">intern</span><span class="params">(Symbol* symbol, TRAPS)</span></span>;</span><br><span class="line">  <span class="function"><span class="type">static</span> oop <span class="title">intern</span><span class="params">(oop string, TRAPS)</span></span>;</span><br><span class="line">  <span class="function"><span class="type">static</span> oop <span class="title">intern</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *utf8_string, TRAPS)</span></span>;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// Rehash the symbol table if it gets out of balance</span></span><br><span class="line">  <span class="function"><span class="type">static</span> <span class="type">void</span> <span class="title">rehash_table</span><span class="params">()</span></span>;</span><br><span class="line">  <span class="function"><span class="type">static</span> <span class="type">bool</span> <span class="title">needs_rehashing</span><span class="params">()</span> </span>&#123; <span class="keyword">return</span> _needs_rehashing; &#125;</span><br><span class="line"></span><br><span class="line">&#125;;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span> <span class="comment">// SHARE_VM_CLASSFILE_SYMBOLTABLE_HPP</span></span></span><br></pre></td></tr></table></figure><p>我将一些常见的方法保留了下来, 我们看到<code>StringTable</code>继承自<code>RehashableHashtable</code>. 数据的存储其实还是存储在<code>RehashableHashtable</code>中.</p><p>需要注意的是<code>StringTable</code>内部的<code>_the_table</code>实例及其创建过程</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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">static</span> StringTable* _the_table;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="type">static</span> <span class="type">void</span> <span class="title">create_table</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="built_in">assert</span>(_the_table == <span class="literal">NULL</span>, <span class="string">&quot;One string table allowed.&quot;</span>);</span><br><span class="line">    _the_table = <span class="keyword">new</span> <span class="built_in">StringTable</span>();</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><p><code>StringTable</code>最终是通过自身的一个静态属性持有.</p><p><code>create_table()</code>是在<code>universe.cpp</code>文件的<code>universe_init()</code>进行调用的.</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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="comment">// openjdk-jdk8u-jdk8u/hotspot/src/share/vm/memory/universe.cpp</span></span><br><span class="line"></span><br><span class="line"><span class="function">jint <span class="title">universe_init</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="comment">// ... 忽略部分代码</span></span><br><span class="line">  JavaClasses::<span class="built_in">compute_hard_coded_offsets</span>();</span><br><span class="line"></span><br><span class="line">  jint status = Universe::<span class="built_in">initialize_heap</span>();</span><br><span class="line">  <span class="keyword">if</span> (status != JNI_OK) &#123;</span><br><span class="line">    <span class="keyword">return</span> status;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  Metaspace::<span class="built_in">global_initialize</span>();</span><br><span class="line"></span><br><span class="line">  <span class="comment">// Create memory for metadata.  Must be after initializing heap for</span></span><br><span class="line">  <span class="comment">// DumpSharedSpaces.</span></span><br><span class="line">  ClassLoaderData::<span class="built_in">init_null_class_loader_data</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// ... 忽略部分代码</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (UseSharedSpaces) &#123;</span><br><span class="line">    <span class="comment">// Read the data structures supporting the shared spaces (shared</span></span><br><span class="line">    <span class="comment">// system dictionary, symbol table, etc.).  After that, access to</span></span><br><span class="line">    <span class="comment">// the file (other than the mapped regions) is no longer needed, and</span></span><br><span class="line">    <span class="comment">// the file is closed. Closing the file does not affect the</span></span><br><span class="line">    <span class="comment">// currently mapped regions.</span></span><br><span class="line">    MetaspaceShared::<span class="built_in">initialize_shared_spaces</span>();</span><br><span class="line">    StringTable::<span class="built_in">create_table</span>();</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    SymbolTable::<span class="built_in">create_table</span>();</span><br><span class="line">    StringTable::<span class="built_in">create_table</span>();</span><br><span class="line">    ClassLoader::<span class="built_in">create_package_info_table</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (DumpSharedSpaces) &#123;</span><br><span class="line">      MetaspaceShared::<span class="built_in">prepare_for_dumping</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">return</span> JNI_OK;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>创建完<code>StringTable</code>之后, 就可以直接通过<code>StringTable</code>的静态方法对其进行操作了.</p>]]></content>
    
    
      
      
    <summary type="html">&lt;script src=&quot;/assets/asciinema-player.js&quot;&gt;&lt;/script&gt;&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;/assets/asciinema-player.css&quot; /&gt;
&lt;blockquote</summary>
      
    
    
    
    <category term="Java" scheme="https://wangmingco.github.io/categories/Java/"/>
    
    
    <category term="JavaSE" scheme="https://wangmingco.github.io/tags/JavaSE/"/>
    
  </entry>
  
  <entry>
    <title>SQL 手册</title>
    <link href="https://wangmingco.github.io/2019/06/20/%E6%95%B0%E6%8D%AE%E5%BA%93/sql/"/>
    <id>https://wangmingco.github.io/2019/06/20/%E6%95%B0%E6%8D%AE%E5%BA%93/sql/</id>
    <published>2019-06-20T00:00:00.000Z</published>
    <updated>2025-12-19T01:50:58.674Z</updated>
    
    <content type="html"><![CDATA[<script src="/assets/asciinema-player.js"></script><link rel="stylesheet" type="text/css" href="/assets/asciinema-player.css" /><p><img src="https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/images/sql/sql_cheat_sheet0.jpeg"></p><p><img src="https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/images/sql/sql_cheat_sheet1.jpeg"></p><p><img src="https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/images/sql/sql_cheat_sheet2.jpeg"></p><p><img src="https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/images/sql/sql_joins_cheat_sheet0.jpeg"></p><p><img src="https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/images/sql/sql_joins_cheat_sheet1.png"></p><p><img src="https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/images/sql/sql_window_functions_cheat_sheet0.jpeg"></p><p><img src="https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/images/sql/sql_window_functions_cheat_sheet1.jpeg"></p>]]></content>
    
    
      
      
    <summary type="html">&lt;script src=&quot;/assets/asciinema-player.js&quot;&gt;&lt;/script&gt;&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;/assets/asciinema-player.css&quot; /&gt;
&lt;p&gt;&lt;img src</summary>
      
    
    
    
    <category term="数据库" scheme="https://wangmingco.github.io/categories/%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
    
    
  </entry>
  
  <entry>
    <title>Java NIO原理剖析之 磁盘IO</title>
    <link href="https://wangmingco.github.io/2019/05/28/Java/io/Java%20NIO%E5%8E%9F%E7%90%86%E5%89%96%E6%9E%90%E4%B9%8B%20%E7%A3%81%E7%9B%98IO/"/>
    <id>https://wangmingco.github.io/2019/05/28/Java/io/Java%20NIO%E5%8E%9F%E7%90%86%E5%89%96%E6%9E%90%E4%B9%8B%20%E7%A3%81%E7%9B%98IO/</id>
    <published>2019-05-28T00:00:00.000Z</published>
    <updated>2025-12-19T01:50:58.654Z</updated>
    
    <content type="html"><![CDATA[<script src="/assets/asciinema-player.js"></script><link rel="stylesheet" type="text/css" href="/assets/asciinema-player.css" /><p>网络上有很多写NIO的文章, 但是大多讲的网络那一块, 我想像前俩次那样先从磁盘IO讲起, 然后慢慢过渡到网络IO, 再慢慢地进入socket, epoll, 能有这样一个循序渐进的过程.</p><p>仍然从一个读文件的小demo程序开始入手分析</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TestFileChannel</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> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line"><span class="type">FileInputStream</span> <span class="variable">inputStream</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FileInputStream</span>(<span class="string">&quot;./10bytes.txt&quot;</span>);</span><br><span class="line"><span class="type">FileChannel</span> <span class="variable">channel</span> <span class="operator">=</span> inputStream.getChannel();</span><br><span class="line">                <span class="comment">// 分配一个10byte的ByteBuffer.</span></span><br><span class="line"><span class="type">ByteBuffer</span> <span class="variable">byteBuffer</span> <span class="operator">=</span> ByteBuffer.allocate(<span class="number">10</span>);</span><br><span class="line">channel.read(byteBuffer);</span><br><span class="line">                <span class="comment">// TODO close</span></span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>看一下getChannel()实现</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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> FileChannel <span class="title function_">getChannel</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">if</span> (channel == <span class="literal">null</span>) &#123;</span><br><span class="line">             channel = FileChannelImpl.open(fd, path, <span class="literal">true</span>, <span class="literal">false</span>, <span class="built_in">this</span>);</span><br><span class="line">         &#125;</span><br><span class="line">         <span class="keyword">return</span> channel;</span><br><span class="line">     &#125;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure><p><code>getChannel()</code>的源码很简单, 就是直接生成了一个<code>FileChannelImpl</code>实例</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">// 下列内容在openjdk-jdk8u-jdk8u/jdk/src/share/classes/sun/nio/ch/FileChannelImpl.java</span></span><br><span class="line">  </span><br><span class="line"><span class="comment">// Used by FileInputStream.getChannel() and RandomAccessFile.getChannel()</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="type">static</span> FileChannel <span class="title">open</span><span class="params">(FileDescriptor fd, String path,</span></span></span><br><span class="line"><span class="params"><span class="function">                                   boolean readable, boolean writable,</span></span></span><br><span class="line"><span class="params"><span class="function">                                   Object parent)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">FileChannelImpl</span>(fd, path, readable, writable, <span class="literal">false</span>, parent);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/images/javase/nio1.jpg"></p><p>通过上面的UML类图, 可以看到FileChannel主要是定义了一些抽象方法, 真正的读写实现是在FileChannelImpl中实现的.</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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="comment">// 下列内容在openjdk-jdk8u-jdk8u/jdk/src/share/classes/sun/nio/ch/FileChannelImpl.java</span></span><br><span class="line">  </span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="type">int</span> <span class="title">read</span><span class="params">(ByteBuffer dst)</span> throws IOException </span>&#123;</span><br><span class="line">        <span class="built_in">ensureOpen</span>();</span><br><span class="line">        <span class="keyword">if</span> (!readable)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">NonReadableChannelException</span>();</span><br><span class="line">        <span class="built_in">synchronized</span> (positionLock) &#123;</span><br><span class="line">            <span class="type">int</span> n = <span class="number">0</span>;</span><br><span class="line">            <span class="type">int</span> ti = <span class="number">-1</span>;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="built_in">begin</span>();</span><br><span class="line">                ti = threads.<span class="built_in">add</span>();</span><br><span class="line">                <span class="keyword">if</span> (!<span class="built_in">isOpen</span>())</span><br><span class="line">                    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">                <span class="keyword">do</span> &#123;</span><br><span class="line">                    n = IOUtil.<span class="built_in">read</span>(fd, dst, <span class="number">-1</span>, nd);</span><br><span class="line">                &#125; <span class="keyword">while</span> ((n == IOStatus.INTERRUPTED) &amp;&amp; <span class="built_in">isOpen</span>());</span><br><span class="line">                <span class="keyword">return</span> IOStatus.<span class="built_in">normalize</span>(n);</span><br><span class="line">            &#125; finally &#123;</span><br><span class="line">                threads.<span class="built_in">remove</span>(ti);</span><br><span class="line">                <span class="built_in">end</span>(n &gt; <span class="number">0</span>);</span><br><span class="line">                assert IOStatus.<span class="built_in">check</span>(n);</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>可以看到真正的IO逻辑调用是 <code>IOUtil.read(fd, dst,-1, nd)</code></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 下列内容在 openjdk-jdk8u-jdk8u/jdk/src/share/classes/sun/nio/ch/IOUtil.java</span></span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="type">static</span> <span class="type">int</span> <span class="title">read</span><span class="params">(FileDescriptor fd, ByteBuffer dst, <span class="type">long</span> position,</span></span></span><br><span class="line"><span class="params"><span class="function">                    NativeDispatcher nd)</span></span></span><br><span class="line"><span class="function">        throws IOException</span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (dst.<span class="built_in">isReadOnly</span>())</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">IllegalArgumentException</span>(<span class="string">&quot;Read-only buffer&quot;</span>);</span><br><span class="line">        <span class="keyword">if</span> (dst instanceof DirectBuffer)</span><br><span class="line">            <span class="keyword">return</span> <span class="built_in">readIntoNativeBuffer</span>(fd, dst, position, nd);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 申请一个临时的DirectBuffer</span></span><br><span class="line">        ByteBuffer bb = Util.<span class="built_in">getTemporaryDirectBuffer</span>(dst.<span class="built_in">remaining</span>());</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">int</span> n = <span class="built_in">readIntoNativeBuffer</span>(fd, bb, position, nd);</span><br><span class="line">            bb.<span class="built_in">flip</span>();</span><br><span class="line">            <span class="keyword">if</span> (n &gt; <span class="number">0</span>)</span><br><span class="line">                dst.<span class="built_in">put</span>(bb);</span><br><span class="line">            <span class="keyword">return</span> n;</span><br><span class="line">        &#125; finally &#123;</span><br><span class="line">            Util.<span class="built_in">offerFirstTemporaryDirectBuffer</span>(bb);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>通过上面的代码可以清楚的看到整个读操作流程:</p><ol><li>如果<code>ByteBuffer</code>是<code>DirectBuffer</code>类型的, 则直接调用<code>readIntoNativeBuffer()</code>函数, 将数据读入进dst里面去</li><li>如果<code>ByteBuffer</code>不是<code>DirectBuffer</code>类型的, 则先申请一个临时的<code>DirectBuffer bb</code></li><li>然后调用<code>readIntoNativeBuffer()</code>函数, 将数据读入到<code>bb</code>里面去</li><li>如果读取到的数量大于0, 则将临时<code>DirectBuffer</code>里的数据拷贝到目标<code>ByteBuffer dst</code>里面去</li><li>最后将<code>DirectBuffer</code>释放掉或者放入一个buffer cache中</li></ol><p>第二步为什么要申请一个临时的directbuffer呢？因为directbuffer不会受到gc的直接管理。如果我们直接使用heapbuffer，当gc的时候会对heapbuffer里面的内容进行移动。</p><p><code>Util.getTemporaryDirectBuffer()</code> 和<code>offerFirstTemporaryDirectBuffer()</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> ByteBuffer <span class="title function_">getTemporaryDirectBuffer</span><span class="params">(<span class="type">int</span> size)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (isBufferTooLarge(size)) &#123;</span><br><span class="line">        <span class="keyword">return</span> ByteBuffer.allocateDirect(size);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">BufferCache</span> <span class="variable">cache</span> <span class="operator">=</span> bufferCache.get();</span><br><span class="line">    <span class="type">ByteBuffer</span> <span class="variable">buf</span> <span class="operator">=</span> cache.get(size);</span><br><span class="line">    <span class="keyword">if</span> (buf != <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> buf;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// No suitable buffer in the cache so we need to allocate a new</span></span><br><span class="line">        <span class="comment">// one. To avoid the cache growing then we remove the first</span></span><br><span class="line">        <span class="comment">// buffer from the cache and free it.</span></span><br><span class="line">        <span class="keyword">if</span> (!cache.isEmpty()) &#123;</span><br><span class="line">            buf = cache.removeFirst();</span><br><span class="line">            free(buf);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ByteBuffer.allocateDirect(size);</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">static</span> <span class="keyword">void</span> <span class="title function_">offerFirstTemporaryDirectBuffer</span><span class="params">(ByteBuffer buf)</span> &#123;</span><br><span class="line">        <span class="comment">// If the buffer is too large for the cache we don&#x27;t have to</span></span><br><span class="line">        <span class="comment">// check the cache. We&#x27;ll just free it.</span></span><br><span class="line">        <span class="keyword">if</span> (isBufferTooLarge(buf)) &#123;</span><br><span class="line">            free(buf);</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="keyword">assert</span> buf != <span class="literal">null</span>;</span><br><span class="line">        <span class="type">BufferCache</span> <span class="variable">cache</span> <span class="operator">=</span> bufferCache.get();</span><br><span class="line">        <span class="keyword">if</span> (!cache.offerFirst(buf)) &#123;</span><br><span class="line">            <span class="comment">// cache is full</span></span><br><span class="line">            free(buf);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>下来看一下<code>readIntoNativeBuffer()</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">readIntoNativeBuffer</span><span class="params">(FileDescriptor fd, ByteBuffer bb,</span></span><br><span class="line"><span class="params">                                            <span class="type">long</span> position, NativeDispatcher nd)</span></span><br><span class="line">        <span class="keyword">throws</span> IOException</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">pos</span> <span class="operator">=</span> bb.position();</span><br><span class="line">        <span class="type">int</span> <span class="variable">lim</span> <span class="operator">=</span> bb.limit();</span><br><span class="line">        <span class="keyword">assert</span> (pos &lt;= lim);</span><br><span class="line">        <span class="type">int</span> <span class="variable">rem</span> <span class="operator">=</span> (pos &lt;= lim ? lim - pos : <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (rem == <span class="number">0</span>)</span><br><span class="line">            <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">n</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">if</span> (position != -<span class="number">1</span>) &#123;</span><br><span class="line">            n = nd.pread(fd, ((DirectBuffer)bb).address() + pos,</span><br><span class="line">                         rem, position);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            n = nd.read(fd, ((DirectBuffer)bb).address() + pos, rem);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (n &gt; <span class="number">0</span>)</span><br><span class="line">            bb.position(pos + n);</span><br><span class="line">        <span class="keyword">return</span> n;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>我们看到最终是通过调用<code>NativeDispatcher</code>的<code>read</code>相关方法实现的读操作.</p><p><img src="https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/images/javase/nio2.jpg"></p><p>因为我们是在分析文件IO, 因此直接看一下<code>FileDispatcherImpl</code>的实现</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// openjdk-jdk8u-jdk8u/jdk/src/solaris/classes/sun/nio/ch/FileDispatcherImpl.java</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">FileDispatcherImpl</span> extends FileDispatcher &#123;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">read</span><span class="params">(FileDescriptor fd, <span class="type">long</span> address, <span class="type">int</span> len)</span> throws IOException </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">read0</span>(fd, address, len);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">pread</span><span class="params">(FileDescriptor fd, <span class="type">long</span> address, <span class="type">int</span> len, <span class="type">long</span> position)</span></span></span><br><span class="line"><span class="function">        throws IOException</span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">pread0</span>(fd, address, len, position);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">long</span> <span class="title">readv</span><span class="params">(FileDescriptor fd, <span class="type">long</span> address, <span class="type">int</span> len)</span> throws IOException </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">readv0</span>(fd, address, len);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">write</span><span class="params">(FileDescriptor fd, <span class="type">long</span> address, <span class="type">int</span> len)</span> throws IOException </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">write0</span>(fd, address, len);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">pwrite</span><span class="params">(FileDescriptor fd, <span class="type">long</span> address, <span class="type">int</span> len, <span class="type">long</span> position)</span></span></span><br><span class="line"><span class="function">        throws IOException</span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">pwrite0</span>(fd, address, len, position);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">long</span> <span class="title">writev</span><span class="params">(FileDescriptor fd, <span class="type">long</span> address, <span class="type">int</span> len)</span></span></span><br><span class="line"><span class="function">        throws IOException</span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">writev0</span>(fd, address, len);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// -- Native methods --</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">static</span> native <span class="type">int</span> <span class="title">read0</span><span class="params">(FileDescriptor fd, <span class="type">long</span> address, <span class="type">int</span> len)</span></span></span><br><span class="line"><span class="function">        throws IOException</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">static</span> native <span class="type">int</span> <span class="title">pread0</span><span class="params">(FileDescriptor fd, <span class="type">long</span> address, <span class="type">int</span> len,</span></span></span><br><span class="line"><span class="params"><span class="function">                             <span class="type">long</span> position)</span> throws IOException</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">static</span> native <span class="type">long</span> <span class="title">readv0</span><span class="params">(FileDescriptor fd, <span class="type">long</span> address, <span class="type">int</span> len)</span></span></span><br><span class="line"><span class="function">        throws IOException</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">static</span> native <span class="type">int</span> <span class="title">write0</span><span class="params">(FileDescriptor fd, <span class="type">long</span> address, <span class="type">int</span> len)</span></span></span><br><span class="line"><span class="function">        throws IOException</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">static</span> native <span class="type">int</span> <span class="title">pwrite0</span><span class="params">(FileDescriptor fd, <span class="type">long</span> address, <span class="type">int</span> len,</span></span></span><br><span class="line"><span class="params"><span class="function">                             <span class="type">long</span> position)</span> throws IOException</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">static</span> native <span class="type">long</span> <span class="title">writev0</span><span class="params">(FileDescriptor fd, <span class="type">long</span> address, <span class="type">int</span> len)</span></span></span><br><span class="line"><span class="function">        throws IOException</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>FileDispatcherImpl</code>中定义了大量的native方法, 相关的实现还是在native方法中.</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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="comment">// openjdk-jdk8u-jdk8u_vscode/jdk/src/solaris/native/sun/nio/ch/FileDispatcherImpl.c</span></span><br><span class="line"></span><br><span class="line"><span class="function">JNIEXPORT jint JNICALL</span></span><br><span class="line"><span class="function"><span class="title">Java_sun_nio_ch_FileDispatcherImpl_read0</span><span class="params">(JNIEnv *env, jclass clazz,</span></span></span><br><span class="line"><span class="params"><span class="function">                             jobject fdo, jlong address, jint len)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    jint fd = <span class="built_in">fdval</span>(env, fdo);</span><br><span class="line">    <span class="type">void</span> *buf = (<span class="type">void</span> *)<span class="built_in">jlong_to_ptr</span>(address);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">convertReturnVal</span>(env, <span class="built_in">read</span>(fd, buf, len), JNI_TRUE);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>通过和 nd.read(fd,((DirectBuffer)bb).address()+ pos, rem); 这一段程序对比可以得到</p><ol><li><code>fdo</code> 就是 <code>FileDescriptor fd</code></li><li><code>address</code> 就是 <code>((DirectBuffer)bb).address()+ pos</code></li><li><code>len</code> 就是<code>rem</code>, <code>(pos &lt;= lim ? lim - pos :0);</code></li></ol><p><code>read(fd, buf, len)</code> 这段程序就是将剩余的len长度的数据读取进<code>DirectBuffer</code>的剩余的空间中.</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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="comment">// openjdk-jdk8u-jdk8u/jdk/src/windows/native/sun/nio/ch/IOUtil.c</span></span><br><span class="line"></span><br><span class="line"><span class="function">jint</span></span><br><span class="line"><span class="function"><span class="title">convertReturnVal</span><span class="params">(JNIEnv *env, jint n, jboolean reading)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (n &gt; <span class="number">0</span>) <span class="comment">/* Number of bytes written */</span></span><br><span class="line">        <span class="keyword">return</span> n;</span><br><span class="line">    <span class="keyword">if</span> (n == <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (reading) &#123;</span><br><span class="line">            <span class="keyword">return</span> IOS_EOF; <span class="comment">/* EOF is -1 in javaland */</span></span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">JNU_ThrowIOExceptionWithLastError</span>(env, <span class="string">&quot;Read/write failed&quot;</span>);</span><br><span class="line">    <span class="keyword">return</span> IOS_THROWN;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>从上面的例子, 我们可以看出, 在NIO读的时候, 我们应该直接分配一个<code>DirectBytebuffer</code>,而不是分配一个<code>HeapByteBuffer</code>, 那样一来就可以减少一次内存拷贝。</p><hr><p>预告一点东西, <code>FileChannel</code> 并不能像<code>SocketChannel</code>一样可以设置成非阻塞模式, 其实是 <code>SelectableChannel</code> 接口中定义了 <code>configureBlocking()</code> 方法</p><p><img src="https://raw.githubusercontent.com/wangmingco/wangmingco.github.io/main/static/images/javase/nio3.jpg"></p><hr><p>在刚开始的read方法中有如下一段</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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">try</span> &#123;</span><br><span class="line">      begin();</span><br><span class="line">      ti = threads.add();</span><br><span class="line">      <span class="keyword">if</span> (!isOpen())</span><br><span class="line">          <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">      <span class="keyword">do</span> &#123;</span><br><span class="line">          n = IOUtil.read(fd, dst, -<span class="number">1</span>, nd);</span><br><span class="line">      &#125; <span class="keyword">while</span> ((n == IOStatus.INTERRUPTED) &amp;&amp; isOpen());</span><br><span class="line">      <span class="keyword">return</span> IOStatus.normalize(n);</span><br><span class="line">  &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">      threads.remove(ti);</span><br><span class="line">      end(n &gt; <span class="number">0</span>);</span><br><span class="line">      <span class="keyword">assert</span> IOStatus.check(n);</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><p><code>begin();</code> 和 <code>end(n &gt;0);</code> 这俩段代码参考</p>]]></content>
    
    
      
      
    <summary type="html">&lt;script src=&quot;/assets/asciinema-player.js&quot;&gt;&lt;/script&gt;&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;/assets/asciinema-player.css&quot; /&gt;
&lt;p&gt;网络上有很多写N</summary>
      
    
    
    
    <category term="Java" scheme="https://wangmingco.github.io/categories/Java/"/>
    
    
    <category term="Java IO" scheme="https://wangmingco.github.io/tags/Java-IO/"/>
    
  </entry>
  
</feed>
