<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>MaphicalYng</title>
  
  <subtitle>有的人一生致力于拥有，有的人一生致力于有所作为。</subtitle>
  <link href="https://maphical.cn/atom.xml" rel="self"/>
  
  <link href="https://maphical.cn/"/>
  <updated>2025-10-11T20:14:47.410Z</updated>
  <id>https://maphical.cn/</id>
  
  <author>
    <name>MaphicalYng</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>JMX 简要介绍</title>
    <link href="https://maphical.cn/2025/10/jmx-intro/"/>
    <id>https://maphical.cn/2025/10/jmx-intro/</id>
    <published>2025-10-11T18:02:02.000Z</published>
    <updated>2025-10-11T20:14:47.410Z</updated>
    
    <content type="html"><![CDATA[<h1 id="1-简介"><a href="#1-简介" class="headerlink" title="1. 简介"></a>1. 简介</h1><p>JMX（Java Management Extensions）是 Java 提供的一种用于管理和监控应用程序、系统对象及资源的技术框架。它通过 MBean（Managed Bean）实现对资源的动态管理，支持远程监控和配置调整，广泛应用于性能调优、故障排查和运维管理场景。</p><h2 id="1-1-JMX-的核心价值"><a href="#1-1-JMX-的核心价值" class="headerlink" title="1.1 JMX 的核心价值"></a>1.1 JMX 的核心价值</h2><ul><li><strong>运行时管理</strong>：无需重启应用即可调整配置参数</li><li><strong>性能监控</strong>：实时查看应用状态和性能指标</li><li><strong>故障排查</strong>：通过JMX工具快速定位问题</li><li><strong>运维自动化</strong>：支持脚本化管理和监控</li></ul><h1 id="2-基本使用"><a href="#2-基本使用" class="headerlink" title="2. 基本使用"></a>2. 基本使用</h1><p>结合 Spring 可以便捷使用：</p><p>相关注解：</p><ul><li><code>org.springframework.jmx.export.annotation.ManagedResource</code> - 标记MBean类</li><li><code>org.springframework.jmx.export.annotation.ManagedAttribute</code> - 标记属性</li><li><code>org.springframework.jmx.export.annotation.ManagedOperation</code> - 标记操作</li><li><code>org.springframework.jmx.export.annotation.ManagedOperationParameters</code> - 标记操作参数</li><li><code>org.springframework.jmx.export.annotation.ManagedOperationParameter</code> - 标记单个参数</li></ul><p>在Spring Boot应用中，需要启用JMX支持：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-comment"># application.yml</span><br><span class="hljs-attr">management:</span><br>  <span class="hljs-attr">endpoints:</span><br>    <span class="hljs-attr">web:</span><br>      <span class="hljs-attr">exposure:</span><br>        <span class="hljs-attr">include:</span> <span class="hljs-string">&quot;*&quot;</span><br>  <span class="hljs-attr">endpoint:</span><br>    <span class="hljs-attr">jmx:</span><br>      <span class="hljs-attr">enabled:</span> <span class="hljs-literal">true</span><br></code></pre></td></tr></table></figure><p>或者通过JVM参数启动：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><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><code class="hljs bash">-Dcom.sun.management.jmxremote<br>-Dcom.sun.management.jmxremote.port=9999<br>-Dcom.sun.management.jmxremote.authenticate=<span class="hljs-literal">false</span><br>-Dcom.sun.management.jmxremote.ssl=<span class="hljs-literal">false</span><br></code></pre></td></tr></table></figure><h2 id="2-1-MBean"><a href="#2-1-MBean" class="headerlink" title="2.1 MBean"></a>2.1 MBean</h2><h3 id="2-1-1-MBean-介绍"><a href="#2-1-1-MBean-介绍" class="headerlink" title="2.1.1 MBean 介绍"></a>2.1.1 MBean 介绍</h3><p>MBean（Managed Bean）是 Java 平台中用于管理应用程序组件的一种机制，属于 <strong>Java Management Extensions (JMX)</strong> 技术的核心部分。MBean 是一种标准的、可被管理的 Java 对象，<br>它通过暴露属性和操作来允许外部管理系统（如 JConsole 或其他监控工具）与其交互。</p><h3 id="2-1-2-定义一个-MBean"><a href="#2-1-2-定义一个-MBean" class="headerlink" title="2.1.2 定义一个 MBean"></a>2.1.2 定义一个 MBean</h3><p>在 Spring 框架中定义一个 MBean 非常方便，使用 ManagedResource 注解，并将目标类注册为 Bean 即可，例如：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> com.mk.jmxdemo.jmxdemo.jmx;<br><br><span class="hljs-keyword">import</span> org.springframework.jmx.export.annotation.*;<br><span class="hljs-keyword">import</span> org.springframework.stereotype.Component;<br><br><span class="hljs-keyword">import</span> java.util.HashMap;<br><span class="hljs-keyword">import</span> java.util.Map;<br><span class="hljs-keyword">import</span> java.util.concurrent.atomic.AtomicLong;<br><br><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@author</span> maphicalyng</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@since</span> 2025/10/9</span><br><span class="hljs-comment"> */</span><br><span class="hljs-meta">@ManagedResource(</span><br><span class="hljs-meta">        objectName = &quot;com.mk.jmxdemo.jmxdemo.jmx:name=DataManager&quot;,</span><br><span class="hljs-meta">        description = &quot;数据管理MBean，提供数据存储和统计功能&quot;</span><br><span class="hljs-meta">)</span><br><span class="hljs-meta">@Component</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">DataManager</span> &#123;<br><br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> Map&lt;String, String&gt; data = <span class="hljs-keyword">new</span> <span class="hljs-title class_">HashMap</span>&lt;&gt;();<br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-type">AtomicLong</span> <span class="hljs-variable">accessCount</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">AtomicLong</span>(<span class="hljs-number">0</span>);<br>    <span class="hljs-keyword">private</span> <span class="hljs-type">boolean</span> <span class="hljs-variable">enabled</span> <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;<br><br>    <span class="hljs-meta">@ManagedAttribute(description = &quot;获取访问次数&quot;)</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-type">long</span> <span class="hljs-title function_">getAccessCount</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">return</span> accessCount.get();<br>    &#125;<br><br>    <span class="hljs-meta">@ManagedAttribute(description = &quot;获取启用状态&quot;)</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">isEnabled</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">return</span> enabled;<br>    &#125;<br><br>    <span class="hljs-meta">@ManagedAttribute(description = &quot;设置启用状态&quot;)</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">setEnabled</span><span class="hljs-params">(<span class="hljs-type">boolean</span> enabled)</span> &#123;<br>        <span class="hljs-built_in">this</span>.enabled = enabled;<br>    &#125;<br><br>    <span class="hljs-meta">@ManagedOperation(description = &quot;重置访问计数器&quot;)</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">resetAccessCount</span><span class="hljs-params">()</span> &#123;<br>        accessCount.set(<span class="hljs-number">0</span>);<br>    &#125;<br><br>    <span class="hljs-meta">@ManagedOperation(description = &quot;获取数据大小&quot;)</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-type">int</span> <span class="hljs-title function_">getDataSize</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">return</span> data.size();<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>Spring 会自动将其注册为 MBean，并暴露出去。</p><h2 id="2-2-MBean-Attribute"><a href="#2-2-MBean-Attribute" class="headerlink" title="2.2 MBean Attribute"></a>2.2 MBean Attribute</h2><h3 id="2-2-1-MBean-属性是什么"><a href="#2-2-1-MBean-属性是什么" class="headerlink" title="2.2.1 MBean 属性是什么"></a>2.2.1 MBean 属性是什么</h3><p>MBean 的属性是指该 MBean 中可以被外部访问或修改的状态信息。这些属性通常以 Java 类中的字段形式存在，并通过一组标准化的 getter 和 setter 方法对外暴露。MBean 属性的设计目的是让外部管理系统能够读取或更新 MBean 的状态，而无需直接访问其内部实现。</p><h4 id="属性的特点"><a href="#属性的特点" class="headerlink" title="属性的特点"></a>属性的特点</h4><ol><li><strong>命名规范</strong>：<ul><li>属性名称必须遵循 JavaBeans 命名规范。</li><li>例如，如果属性名为 <code>CacheSize</code>，那么对应的 getter 方法应为 <code>getCacheSize()</code>，setter 方法应为 <code>setCacheSize(int value)</code>。</li></ul></li><li><strong>只读或可写</strong>：<ul><li>如果只有 getter 方法，则该属性是只读的。</li><li>如果同时有 getter 和 setter 方法，则该属性是可读写的。</li></ul></li><li><strong>数据类型</strong>：<ul><li>属性的数据类型可以是基本类型（如 <code>int</code>、<code>boolean</code>）或复杂类型（如自定义类）。</li><li>复杂类型的属性需要确保其类是可序列化的，以便在分布式环境中传递。</li></ul></li><li><strong>元数据描述</strong>：<ul><li>每个属性可以通过 JMX 提供的元数据进行描述，例如属性的名称、类型、描述信息等。这些元数据可以帮助管理工具更好地理解和展示属性。</li></ul></li></ol><h3 id="2-2-2-示例：定义一个简单的-MBean-及其属性"><a href="#2-2-2-示例：定义一个简单的-MBean-及其属性" class="headerlink" title="2.2.2 示例：定义一个简单的 MBean 及其属性"></a>2.2.2 示例：定义一个简单的 MBean 及其属性</h3><p>以下是一个简单的 MBean 示例，展示了如何定义和暴露属性：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> com.mk.jmxdemo.jmxdemo.jmx;<br><br><span class="hljs-keyword">import</span> org.springframework.jmx.export.annotation.*;<br><span class="hljs-keyword">import</span> org.springframework.stereotype.Component;<br><br><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@author</span> maphicalyng</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@since</span> 2025/10/9</span><br><span class="hljs-comment"> */</span><br><span class="hljs-meta">@ManagedResource(</span><br><span class="hljs-meta">        objectName = &quot;com.mk.jmxdemo.jmxdemo.jmx:name=ConfigManager&quot;,</span><br><span class="hljs-meta">        description = &quot;配置管理MBean示例&quot;</span><br><span class="hljs-meta">)</span><br><span class="hljs-meta">@Component</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">ConfigManager</span> &#123;<br><br>    <span class="hljs-keyword">private</span> <span class="hljs-type">String</span> <span class="hljs-variable">appName</span> <span class="hljs-operator">=</span> <span class="hljs-string">&quot;JMX Demo App&quot;</span>;<br>    <span class="hljs-keyword">private</span> <span class="hljs-type">String</span> <span class="hljs-variable">version</span> <span class="hljs-operator">=</span> <span class="hljs-string">&quot;1.0.0&quot;</span>;<br>    <span class="hljs-keyword">private</span> <span class="hljs-type">boolean</span> <span class="hljs-variable">debugMode</span> <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>;<br>    <span class="hljs-keyword">private</span> <span class="hljs-type">int</span> <span class="hljs-variable">maxConnections</span> <span class="hljs-operator">=</span> <span class="hljs-number">100</span>;<br><br>    <span class="hljs-meta">@ManagedAttribute(description = &quot;获取应用名称&quot;)</span><br>    <span class="hljs-keyword">public</span> String <span class="hljs-title function_">getAppName</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">return</span> appName;<br>    &#125;<br><br>    <span class="hljs-meta">@ManagedAttribute(description = &quot;设置应用名称&quot;)</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">setAppName</span><span class="hljs-params">(String appName)</span> &#123;<br>        <span class="hljs-built_in">this</span>.appName = appName;<br>    &#125;<br><br>    <span class="hljs-meta">@ManagedAttribute(description = &quot;获取应用版本（只读）&quot;)</span><br>    <span class="hljs-keyword">public</span> String <span class="hljs-title function_">getVersion</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">return</span> version;<br>    &#125;<br><br>    <span class="hljs-meta">@ManagedAttribute(description = &quot;获取调试模式状态&quot;)</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">isDebugMode</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">return</span> debugMode;<br>    &#125;<br><br>    <span class="hljs-meta">@ManagedAttribute(description = &quot;设置调试模式&quot;)</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">setDebugMode</span><span class="hljs-params">(<span class="hljs-type">boolean</span> debugMode)</span> &#123;<br>        <span class="hljs-built_in">this</span>.debugMode = debugMode;<br>    &#125;<br><br>    <span class="hljs-meta">@ManagedAttribute(description = &quot;获取最大连接数&quot;)</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-type">int</span> <span class="hljs-title function_">getMaxConnections</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">return</span> maxConnections;<br>    &#125;<br><br>    <span class="hljs-meta">@ManagedAttribute(description = &quot;设置最大连接数&quot;)</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">setMaxConnections</span><span class="hljs-params">(<span class="hljs-type">int</span> maxConnections)</span> &#123;<br>        <span class="hljs-keyword">if</span> (maxConnections &gt; <span class="hljs-number">0</span>) &#123;<br>            <span class="hljs-built_in">this</span>.maxConnections = maxConnections;<br>        &#125;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>在这个示例中：</p><ul><li><code>version</code> 是一个只读属性，因为只有 <code>getVersion()</code> 方法。</li><li><code>appName</code>、<code>debugMode</code>、<code>maxConnections</code> 是可读写属性，因为它们既有 getter 方法，也有 setter 方法。</li></ul><h2 id="2-3-MBean-Operation"><a href="#2-3-MBean-Operation" class="headerlink" title="2.3 MBean Operation"></a>2.3 MBean Operation</h2><p>MBean 操作（MBean Operation）是 MBean 暴露给外部系统的可调用的方法。这些方法允许管理员或监控系统在应用运行时执行特定的管理性任务，比如清空缓存、重启服务、重新加载配置等。</p><p>在 Spring 中，使用 ManagedOperation 注解标注，使用 ManagedParameters 注解标记操作接收的参数，例如：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> com.mk.jmxdemo.jmxdemo.jmx;<br><br><span class="hljs-keyword">import</span> jakarta.annotation.PostConstruct;<br><span class="hljs-keyword">import</span> org.springframework.jmx.export.annotation.*;<br><span class="hljs-keyword">import</span> org.springframework.stereotype.Component;<br><br><span class="hljs-keyword">import</span> java.util.HashMap;<br><span class="hljs-keyword">import</span> java.util.Map;<br><span class="hljs-keyword">import</span> java.util.concurrent.atomic.AtomicLong;<br><br><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@author</span> maphicalyng</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@since</span> 2025/10/9</span><br><span class="hljs-comment"> */</span><br><span class="hljs-meta">@ManagedResource(</span><br><span class="hljs-meta">        objectName = &quot;com.mk.jmxdemo.jmxdemo.jmx:name=DataManager&quot;,</span><br><span class="hljs-meta">        description = &quot;数据管理MBean，提供数据存储和统计功能&quot;</span><br><span class="hljs-meta">)</span><br><span class="hljs-meta">@Component</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">DataManager</span> &#123;<br><br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> Map&lt;String, String&gt; data = <span class="hljs-keyword">new</span> <span class="hljs-title class_">HashMap</span>&lt;&gt;();<br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-type">AtomicLong</span> <span class="hljs-variable">accessCount</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">AtomicLong</span>(<span class="hljs-number">0</span>);<br><br>    <span class="hljs-meta">@PostConstruct</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">init</span><span class="hljs-params">()</span> &#123;<br>        data.put(<span class="hljs-string">&quot;name&quot;</span>, <span class="hljs-string">&quot;maphicalyng&quot;</span>);<br>        data.put(<span class="hljs-string">&quot;age&quot;</span>, <span class="hljs-string">&quot;18&quot;</span>);<br>        data.put(<span class="hljs-string">&quot;gender&quot;</span>, <span class="hljs-string">&quot;male&quot;</span>);<br>        data.put(<span class="hljs-string">&quot;email&quot;</span>, <span class="hljs-string">&quot;maphicalyng@example.com&quot;</span>);<br>        data.put(<span class="hljs-string">&quot;phone&quot;</span>, <span class="hljs-string">&quot;1234567890&quot;</span>);<br>    &#125;<br><br>    <span class="hljs-meta">@ManagedOperation(description = &quot;获取指定数据&quot;)</span><br>    <span class="hljs-meta">@ManagedOperationParameters(</span><br><span class="hljs-meta">            @ManagedOperationParameter(name = &quot;key&quot;, description = &quot;数据键&quot;)</span><br><span class="hljs-meta">    )</span><br>    <span class="hljs-keyword">public</span> String <span class="hljs-title function_">getDataKey</span><span class="hljs-params">(String key)</span> &#123;<br>        accessCount.incrementAndGet();<br>        <span class="hljs-keyword">return</span> data.get(key);<br>    &#125;<br><br>    <span class="hljs-meta">@ManagedOperation(description = &quot;设置指定数据&quot;)</span><br>    <span class="hljs-meta">@ManagedOperationParameters(</span><br><span class="hljs-meta">            value = &#123;</span><br><span class="hljs-meta">                    @ManagedOperationParameter(name = &quot;key&quot;, description = &quot;数据键&quot;),</span><br><span class="hljs-meta">                    @ManagedOperationParameter(name = &quot;value&quot;, description = &quot;数据值&quot;)</span><br><span class="hljs-meta">            &#125;</span><br><span class="hljs-meta">    )</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">setDataKey</span><span class="hljs-params">(String key, String value)</span> &#123;<br>        data.put(key, value);<br>    &#125;<br><br>    <span class="hljs-meta">@ManagedOperation(description = &quot;清空所有数据&quot;)</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">clearAllData</span><span class="hljs-params">()</span> &#123;<br>        data.clear();<br>    &#125;<br><br>    <span class="hljs-meta">@ManagedOperation(description = &quot;获取所有数据键&quot;)</span><br>    <span class="hljs-keyword">public</span> String[] getAllKeys() &#123;<br>        <span class="hljs-keyword">return</span> data.keySet().toArray(<span class="hljs-keyword">new</span> <span class="hljs-title class_">String</span>[<span class="hljs-number">0</span>]);<br>    &#125;<br><br>    <span class="hljs-meta">@ManagedOperation(description = &quot;批量设置数据&quot;)</span><br>    <span class="hljs-meta">@ManagedOperationParameters(</span><br><span class="hljs-meta">            @ManagedOperationParameter(name = &quot;dataMap&quot;, description = &quot;数据映射（JSON格式）&quot;)</span><br><span class="hljs-meta">    )</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">batchSetData</span><span class="hljs-params">(String dataMap)</span> &#123;<br>        <span class="hljs-comment">// 这里可以解析JSON并批量设置数据</span><br>        <span class="hljs-comment">// 实际实现需要JSON解析库</span><br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>其中：</p><ul><li><code>getDataKey</code> 和 <code>setDataKey</code> 是两个 Operation，用于查看和设置 data 的特定 key 的值</li><li><code>clearAllData</code> 用于清空所有数据</li><li><code>getAllKeys</code> 用于获取所有数据键</li><li><code>batchSetData</code> 用于批量设置数据</li></ul><p>在 jconsole 等外部工具中，会显示为一个按钮，并提供输入控件来输入调用参数，点击执行后，会执行对应 Operation 方法中的逻辑。</p><h1 id="3-相关工具"><a href="#3-相关工具" class="headerlink" title="3. 相关工具"></a>3. 相关工具</h1><h2 id="3-1-jconsole"><a href="#3-1-jconsole" class="headerlink" title="3.1 jconsole"></a>3.1 jconsole</h2><p>JDK 自带的图形化工具，用于通过 JMX 管理 Java 系统。</p><p>上述 MBean 在 jconsole 中的操作界面为：</p><p><strong>总览</strong></p><p><img src="/img/jmx-intro/1760031529687-91da4ca8-b70c-4931-a1b9-da115880fedd.png"></p><p><strong>Attributes</strong></p><p><img src="/img/jmx-intro/1760031566966-90641881-cf59-4d58-8b75-d18ecdc804b4.png"></p><p><strong>Operations</strong></p><p><img src="/img/jmx-intro/1760031576528-6049acb3-60be-47b4-b2a7-d34617985b0a.png"></p><p><strong>Notifications</strong></p><p><img src="/img/jmx-intro/1760031617523-e3b7db13-7a8e-4269-9266-b6353fdd9bcf.png"></p><h2 id="3-2-jmxterm"><a href="#3-2-jmxterm" class="headerlink" title="3.2 jmxterm"></a>3.2 jmxterm</h2><p>开源的 JMX 命令行管理工具，当在无图形界面的环境中连接 JMX 时使用，项目地址：</p><p><a href="/link/?t=aa2d71dc" target="_blank">CYCLOPSGROUP DOCS - Jmxterm</a></p><p>使用示例：</p><p><img src="/img/jmx-intro/1760031849975-3b6215c3-ef15-428f-aa5c-db5ef53aac09.png"></p><p>四条命令的说明：</p><ol><li>列出所有 MBeans 的 domain；</li><li>获取 Attribute 的属性值；</li><li>调用 Operation setDataKey，参数为 age、18；</li><li>调用 Operation getData，参数为 age。</li></ol><h1 id="4-最佳实践和注意事项"><a href="#4-最佳实践和注意事项" class="headerlink" title="4. 最佳实践和注意事项"></a>4. 最佳实践和注意事项</h1><h2 id="4-1-性能考虑"><a href="#4-1-性能考虑" class="headerlink" title="4.1 性能考虑"></a>4.1 性能考虑</h2><ul><li><strong>避免频繁调用</strong>：JMX 操作会消耗系统资源，避免在高频场景下使用</li><li><strong>线程安全</strong>：确保 MBean 的属性和操作是线程安全的</li><li><strong>异常处理</strong>：在操作中妥善处理异常，避免影响主业务逻辑</li></ul><h2 id="4-2-安全考虑"><a href="#4-2-安全考虑" class="headerlink" title="4.2 安全考虑"></a>4.2 安全考虑</h2><ul><li><strong>权限控制</strong>：生产环境中应该启用 JMX 认证和 SSL</li><li><strong>敏感信息</strong>：避免在 JMX 中暴露敏感配置信息</li><li><strong>网络隔离</strong>：限制 JMX 端口的网络访问</li></ul><h2 id="4-3-监控建议"><a href="#4-3-监控建议" class="headerlink" title="4.3 监控建议"></a>4.3 监控建议</h2><ul><li><strong>关键指标</strong>：监控应用的核心业务指标</li><li><strong>告警机制</strong>：结合监控系统设置合理的告警阈值</li><li><strong>日志记录</strong>：记录重要的 JMX 操作日志</li></ul><h2 id="4-4-常见问题"><a href="#4-4-常见问题" class="headerlink" title="4.4 常见问题"></a>4.4 常见问题</h2><ol><li><strong>连接失败</strong>：检查 JMX 端口配置和防火墙设置</li><li><strong>权限不足</strong>：确认 JVM 参数中的认证配置</li><li><strong>性能影响</strong>：避免在性能关键路径上使用 JMX</li></ol><hr><p>以上。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;1-简介&quot;&gt;&lt;a href=&quot;#1-简介&quot; class=&quot;headerlink&quot; title=&quot;1. 简介&quot;&gt;&lt;/a&gt;1. 简介&lt;/h1&gt;&lt;p&gt;JMX（Java Management Extensions）是 Java 提供的一种用于管理和监控应用程序、系统对象及</summary>
      
    
    
    
    <category term="Computer Science" scheme="https://maphical.cn/categories/Computer-Science/"/>
    
    <category term="Java" scheme="https://maphical.cn/categories/Computer-Science/Java/"/>
    
    
  </entry>
  
  <entry>
    <title>将 Jar 包构建为 AppImage 可执行文件</title>
    <link href="https://maphical.cn/2025/07/build-jar-to-appimage/"/>
    <id>https://maphical.cn/2025/07/build-jar-to-appimage/</id>
    <published>2025-07-05T16:49:18.000Z</published>
    <updated>2025-10-12T14:42:21.594Z</updated>
    
    <content type="html"><![CDATA[<p>如果你有一个 Jar 包，想要分发到服务器&#x2F;客户&#x2F;桌面等环境，但是又不想这些环境去配置 Java 环境该怎么办呢。本文章将介绍如何将一个 Jar 包打包成 Linux 的通用可执行文件：AppImage。</p><h1 id="dJQta">1.AppImage 介绍</h1><p>AppImage 是一种通用的 Linux 应用打包格式，旨在简化软件分发和安装过程。它允许开发者将应用程序及其所有依赖项打包成一个单独的文件，用户可以直接运行，无需复杂的安装步骤。AppImage 文件以<code>.AppImage</code>为后缀，支持大多数主流 Linux 发行版（如Ubuntu、Fedora、Arch Linux等），具有良好的兼容性和便携性。</p><h2 id="cod3R">1.1 AppImage 的优势</h2><ul><li><strong>跨发行版兼容性</strong>：AppImage 可以在大多数 Linux 发行版上运行，无需针对不同发行版单独打包。</li><li><strong>无需安装</strong>：用户可以直接运行 AppImage 文件，无需使用包管理器安装依赖项。</li><li><strong>简化分发</strong>：开发者只需生成一个文件即可分发应用程序，极大简化了发布流程。</li></ul><h2 id="fUqEf">1.2 快速入门资源</h2><ul><li><strong>官方网站</strong>：<a href="/link/?t=6b338d06" target="_blank">https://appimage.org/</a></li><li><strong>文档</strong>：<a href="/link/?t=9a444c8b" target="_blank">https://docs.appimage.org/</a></li><li><strong>常见问题解答</strong>：<a href="/link/?t=1e5ac1c2" target="_blank">https://discourse.appimage.org/</a></li><li><strong>示例</strong>：<a href="/link/?t=e45d9f8b" target="_blank">https://github.com/AppImage/AppImageKit</a></li></ul><p>通过 AppImage，开发者和用户都可以更轻松地管理和分发应用程序。接下来，我们将详细介绍如何将 Java 应用程序（Jar包）打包成 AppImage 格式。</p><h1 id="H2aYU">2.编写应用并生成 Jar 包</h1><p>Java 程序开发就按照一般方式进行，这里快速介绍一下。可以通过 maven 提供的快捷脚手架生成一个 Java 项目（目录名称为 artifactId）：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">mvn archetype:generate -DgroupId=com.mk.tool -DartifactId=simple-tool -Dversion=1.0.0-SNAPSHOT -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=<span class="hljs-literal">false</span><br></code></pre></td></tr></table></figure><p> 正常编写代码，并生成一个带有全部依赖的 Jar 包，可以使用 <code>maven-assembly-plugin</code> 插件：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">plugin</span>&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.apache.maven.plugins<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>maven-assembly-plugin<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">configuration</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">archive</span>&gt;</span><br>      <span class="hljs-tag">&lt;<span class="hljs-name">manifest</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">mainClass</span>&gt;</span>com.mk.tool.actionagent.Entry<span class="hljs-tag">&lt;/<span class="hljs-name">mainClass</span>&gt;</span><br>      <span class="hljs-tag">&lt;/<span class="hljs-name">manifest</span>&gt;</span><br>    <span class="hljs-tag">&lt;/<span class="hljs-name">archive</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">descriptorRefs</span>&gt;</span><br>      <span class="hljs-tag">&lt;<span class="hljs-name">descriptorRef</span>&gt;</span>jar-with-dependencies<span class="hljs-tag">&lt;/<span class="hljs-name">descriptorRef</span>&gt;</span><br>    <span class="hljs-tag">&lt;/<span class="hljs-name">descriptorRefs</span>&gt;</span><br>  <span class="hljs-tag">&lt;/<span class="hljs-name">configuration</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">plugin</span>&gt;</span><br></code></pre></td></tr></table></figure><p>使用命令行生成 Jar：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">mvn clean package assembly:single<br></code></pre></td></tr></table></figure><p>假设输出的 Jar 包路径为：<code>./target/simple-tool-1.0.0-SNAPSHOT-jar-with-dependencies.jar</code></p><h1 id="IMIBl">3.使用 Jpackage 工具生成需要的资源</h1><h2 id="FiJWj">3.1 Jpackage 是什么</h2><p>JPackage 是 Java Development Kit (JDK) 自 JDK 14 起引入的一个命令行工具，旨在简化将 Java 应用程序打包成平台特定格式的过程。这个工具允许开发者为 Windows、macOS 和 Linux 系统创建安装包或软件包，如 <code>.msi</code> 文件（Windows）、<code>.dmg</code> 文件（macOS）和 <code>.deb</code> 或 <code>.rpm</code> 文件（Linux）。使用 JPackage 可以让最终用户更方便地安装 Java 应用程序，并且可以更好地集成到操作系统的环境中，当然，JPackage 工具也支持打包 AppImage 格式。</p><h3 id="NCHYN">3.1.1 主要功能</h3><ul><li><strong>跨平台支持</strong>：支持生成适用于 Windows、macOS 和 Linux 的安装包。</li><li><strong>自定义图标与描述</strong>：可以设置应用程序的图标以及描述信息等元数据。</li><li><strong>模块化支持</strong>：不仅支持传统的 JAR 文件，还支持基于 Java 模块系统（JPMS）的应用程序。</li><li><strong>依赖管理</strong>：能够处理应用程序及其运行时所需的依赖项。</li><li><strong>启动器生成</strong>：自动创建启动脚本或快捷方式，使得应用更容易被启动。</li><li><strong>更新机制</strong>：提供基础的支持来配置应用程序的自动更新。</li></ul><h3 id="Gu0tG">3.1.2 示例用法</h3><p>使用 JPackage 需要指定几个关键参数，包括输入文件的位置、输出格式、目标操作系统等。下面是一个简单的例子，展示如何使用 JPackage 来创建一个 macOS 的 <code>.dmg</code> 包：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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><code class="hljs bash">jpackage --name MyApp \<br>         --input /path/to/myapp \<br>         --main-jar myapp.jar \<br>         --main-class com.example.MyApp \<br>         --<span class="hljs-built_in">type</span> dmg \<br>         --icon /path/to/icon.icns<br></code></pre></td></tr></table></figure><p>这里：</p><ul><li><code>--name</code> 指定了应用程序的名字。</li><li><code>--input</code> 指向包含所有必要资源的目录。</li><li><code>--main-jar</code> 指出主 JAR 文件。</li><li><code>--main-class</code> 定义了程序的入口点。</li><li><code>--type</code> 设置了输出格式，在此例中为 macOS 的磁盘映像文件。</li><li><code>--icon</code> 用于指定应用图标。</li></ul><h2 id="WQeOz">3.2 创建 AppImage 需要的文件目录</h2><p>执行 JPackage 命令：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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><code class="hljs bash">jpackage \<br>    --input <span class="hljs-string">&quot;./target&quot;</span> \<br>    --main-jar simple-tool-1.0.0-SNAPSHOT-jar-with-dependencies.jar \<br>    --main-class com.mk.tool.simpletool.Entry \<br>    --name simple-tool \<br>    --app-version <span class="hljs-string">&quot;1.0.0-SNAPSHOT&quot;</span> \<br>    --vendor <span class="hljs-string">&quot;MaphicalYng&quot;</span> \<br>    --description <span class="hljs-string">&quot;Simple Tool，示例简单命令行工具。&quot;</span> \<br>    --copyright <span class="hljs-string">&quot;Copyright <span class="hljs-subst">$(date +%Y)</span> MaphicalYng&quot;</span> \<br>    --<span class="hljs-built_in">type</span> app-image \<br>    --dest <span class="hljs-string">&quot;./packages&quot;</span><br></code></pre></td></tr></table></figure><p>命令中各个选项的作用：</p><ul><li><code>--input</code>: 指定包含应用程序资源的目录。在这个例子中，<code>./target</code>是存放构建输出（如JAR文件）的目录。</li><li><code>--main-jar</code>: 指明作为主程序入口点的JAR文件名。这个JAR文件包含了应用及其所有依赖项。</li><li><code>--main-class</code>: 指定Java应用程序的主类。当启动应用时，会运行这个类中的<code>public static void main(String[] args)</code>方法。</li><li><code>--name</code>: 设置生成的应用程序包或可执行文件的名字。</li><li><code>--app-version</code>: 定义应用程序的版本号。</li><li><code>--vendor</code>: 应用程序供应商的信息。</li><li><code>--description</code>: 提供应用程序的简短描述。</li><li><code>--copyright</code>: 添加版权信息到应用程序元数据中。这里使用了shell命令<code>$(date +%Y)</code>来动态插入当前年份。</li><li><code>--type</code>: 指定要创建的打包类型。<code>app-image</code>表示将为Linux系统创建一个自包含的应用镜像，它可以在没有安装 Java 的情况下运行。</li><li><code>--dest</code>: 指定输出目录，即生成的软件包将被放置的位置。</li></ul><p>执行完成之后，将会生成这样一个目录结构和文件：</p><p><img src="/img/build-jar-to-appimage/jpackage-dir-structure.png"></p><p>这个目录就是生成 AppImage 的基础目录，但是还需要进行一些目录结构的调整，让它更匹配标准结构。</p><h2 id="MzEcR">3.3 安装 AppImageTool 和 AppRun</h2><h3 id="hYoRd">3.3.1 简要介绍</h3><p>AppImageTool 是一个用于创建 AppImage 的工具，它允许开发者将应用程序及其依赖项打包成一个可执行文件，从而在不同的 Linux 发行版上轻松分发和运行。AppRun 则是一个轻量级的运行时环境，它可以帮助用户直接从命令行启动 AppImage 文件，简化了 AppImage 的使用流程。</p><p>实际上执行 AppImage 文件就是执行了其中的 AppRun 文件，相当于是整个程序的入口。</p><h3 id="CPskD">3.3.2 下载安装</h3><p>可以使用下面的脚本自动安装在项目目录中（默认下载 x86_64 架构的可执行文件，若你的环境不同可以修改其中的下载 URL），值得一提的是，这两个程序也是使用的 AppImage 格式，这何尝不是一种自举呢：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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><code class="hljs bash"><span class="hljs-meta">#!/bin/sh</span><br><br><span class="hljs-built_in">set</span> -e<br><br><span class="hljs-built_in">echo</span> <span class="hljs-string">&quot;Setting up AppImage tools...&quot;</span><br><br><span class="hljs-comment"># 创建工具目录</span><br>TOOLS_DIR=<span class="hljs-string">&quot;./tools&quot;</span><br><span class="hljs-built_in">mkdir</span> -p <span class="hljs-string">&quot;<span class="hljs-variable">$TOOLS_DIR</span>&quot;</span><br><br><span class="hljs-comment"># 从官方 Github 下载 appimagetool</span><br><span class="hljs-keyword">if</span> [ ! -f <span class="hljs-string">&quot;<span class="hljs-variable">$TOOLS_DIR</span>/appimagetool&quot;</span> ]; <span class="hljs-keyword">then</span><br>    <span class="hljs-built_in">echo</span> <span class="hljs-string">&quot;Downloading appimagetool...&quot;</span><br>    wget -O <span class="hljs-string">&quot;<span class="hljs-variable">$TOOLS_DIR</span>/appimagetool&quot;</span> <span class="hljs-string">&quot;https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage&quot;</span><br>    <span class="hljs-built_in">chmod</span> +x <span class="hljs-string">&quot;<span class="hljs-variable">$TOOLS_DIR</span>/appimagetool&quot;</span><br><span class="hljs-keyword">fi</span><br><br><span class="hljs-comment"># 下载 AppRun</span><br><span class="hljs-keyword">if</span> [ ! -f <span class="hljs-string">&quot;<span class="hljs-variable">$TOOLS_DIR</span>/AppRun&quot;</span> ]; <span class="hljs-keyword">then</span><br>    <span class="hljs-built_in">echo</span> <span class="hljs-string">&quot;Downloading AppRun...&quot;</span><br>    wget -O <span class="hljs-string">&quot;<span class="hljs-variable">$TOOLS_DIR</span>/AppRun&quot;</span> <span class="hljs-string">&quot;https://github.com/AppImage/AppImageKit/releases/download/continuous/AppRun-x86_64&quot;</span><br>    <span class="hljs-built_in">chmod</span> +x <span class="hljs-string">&quot;<span class="hljs-variable">$TOOLS_DIR</span>/AppRun&quot;</span><br><span class="hljs-keyword">fi</span><br><br><span class="hljs-built_in">echo</span> <span class="hljs-string">&quot;AppImage tools setup completed!&quot;</span> <br></code></pre></td></tr></table></figure><h2 id="vLxET">3.3 调整目录结构</h2><p>在正式创建 AppImage 程序包之前，还需要调整一下我们的目录结构以及生成 .desktop 文件，适配 AppRun 工具兼容的标准结构，可以直接执行脚本来生成一个符合条件的目录 <code>appdir</code>：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-meta">#!/bin/sh</span><br><br><span class="hljs-built_in">set</span> -e<br><br><span class="hljs-comment"># 设置目录</span><br>APP_DIR=<span class="hljs-string">&quot;./packages/simple-tool&quot;</span><br>APPDIR_DIR=<span class="hljs-string">&quot;./appdir&quot;</span><br>TOOLS_DIR=<span class="hljs-string">&quot;./tools&quot;</span><br><br><span class="hljs-built_in">echo</span> <span class="hljs-string">&quot;Creating AppDir structure...&quot;</span><br><br><span class="hljs-comment"># 清理并创建 AppDir</span><br><span class="hljs-built_in">rm</span> -rf <span class="hljs-string">&quot;<span class="hljs-variable">$APPDIR_DIR</span>&quot;</span><br><span class="hljs-built_in">mkdir</span> -p <span class="hljs-string">&quot;<span class="hljs-variable">$APPDIR_DIR</span>&quot;</span><br><br><span class="hljs-comment"># 创建标准的 usr 目录结构</span><br><span class="hljs-built_in">mkdir</span> -p <span class="hljs-string">&quot;<span class="hljs-variable">$APPDIR_DIR</span>/usr/bin&quot;</span><br><span class="hljs-built_in">mkdir</span> -p <span class="hljs-string">&quot;<span class="hljs-variable">$APPDIR_DIR</span>/usr/lib&quot;</span><br><br><span class="hljs-comment"># 复制应用文件到正确的位置</span><br><span class="hljs-keyword">if</span> [ -f <span class="hljs-string">&quot;<span class="hljs-variable">$APP_DIR</span>/bin/simple-tool&quot;</span> ]; <span class="hljs-keyword">then</span><br>    <span class="hljs-built_in">cp</span> <span class="hljs-string">&quot;<span class="hljs-variable">$APP_DIR</span>/bin/simple-tool&quot;</span> <span class="hljs-string">&quot;<span class="hljs-variable">$APPDIR_DIR</span>/usr/bin/&quot;</span><br><span class="hljs-keyword">fi</span><br><br><span class="hljs-keyword">if</span> [ -d <span class="hljs-string">&quot;<span class="hljs-variable">$APP_DIR</span>/lib&quot;</span> ]; <span class="hljs-keyword">then</span><br>    <span class="hljs-built_in">cp</span> -r <span class="hljs-string">&quot;<span class="hljs-variable">$APP_DIR</span>/lib&quot;</span>/* <span class="hljs-string">&quot;<span class="hljs-variable">$APPDIR_DIR</span>/usr/lib/&quot;</span><br><span class="hljs-keyword">fi</span><br><br><span class="hljs-comment"># 使用下载的 AppRun 文件</span><br><span class="hljs-built_in">echo</span> <span class="hljs-string">&quot;Using downloaded AppRun...&quot;</span><br><span class="hljs-built_in">cp</span> <span class="hljs-string">&quot;<span class="hljs-variable">$TOOLS_DIR</span>/AppRun&quot;</span> <span class="hljs-string">&quot;<span class="hljs-variable">$APPDIR_DIR</span>/AppRun&quot;</span><br><span class="hljs-built_in">chmod</span> +x <span class="hljs-string">&quot;<span class="hljs-variable">$APPDIR_DIR</span>/AppRun&quot;</span><br><br><span class="hljs-comment"># 创建 .desktop 文件</span><br><span class="hljs-built_in">cat</span> &gt; <span class="hljs-string">&quot;<span class="hljs-variable">$APPDIR_DIR</span>/simple-tool.desktop&quot;</span> &lt;&lt; <span class="hljs-string">EOF</span><br><span class="hljs-string">[Desktop Entry]</span><br><span class="hljs-string">Name=simple-tool</span><br><span class="hljs-string">Comment=Simple Tool，简单的命令行工具。</span><br><span class="hljs-string">Exec=simple-tool</span><br><span class="hljs-string">Icon=simple-tool</span><br><span class="hljs-string">Terminal=true</span><br><span class="hljs-string">Type=Application</span><br><span class="hljs-string">Categories=Utility;</span><br><span class="hljs-string">EOF</span><br><br><span class="hljs-comment"># 复制图标</span><br><span class="hljs-keyword">if</span> [ -f <span class="hljs-string">&quot;<span class="hljs-variable">$APP_DIR</span>/lib/simple-tool.png&quot;</span> ]; <span class="hljs-keyword">then</span><br>    <span class="hljs-built_in">cp</span> <span class="hljs-string">&quot;<span class="hljs-variable">$APP_DIR</span>/lib/simple-tool.png&quot;</span> <span class="hljs-string">&quot;<span class="hljs-variable">$APPDIR_DIR</span>/simple-tool.png&quot;</span><br><span class="hljs-keyword">fi</span><br></code></pre></td></tr></table></figure><p>执行成功后，生成的 appdir 目录结构如下：</p><p><img src="/img/build-jar-to-appimage/appimage-standard-dir-structure.png"></p><p>现在就可以正式创建 AppImage 了。</p><h2 id="lEfpK">3.4 创建 AppImage 可执行文件</h2><p>执行命令：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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><code class="hljs bash"><span class="hljs-meta">#!/bin/sh</span><br><br><span class="hljs-built_in">set</span> -e<br><br><span class="hljs-comment"># 设置目录</span><br>OUTPUT_DIR=<span class="hljs-string">&quot;./output&quot;</span><br>APPDIR_DIR=<span class="hljs-string">&quot;./appdir&quot;</span><br>TOOLS_DIR=<span class="hljs-string">&quot;./tools&quot;</span><br>APP_VERSION=<span class="hljs-string">&quot;1.0.0&quot;</span><br><br><span class="hljs-built_in">echo</span> <span class="hljs-string">&quot;Building AppImage...&quot;</span><br><br><span class="hljs-comment"># 创建输出目录</span><br><span class="hljs-built_in">rm</span> -rf <span class="hljs-string">&quot;<span class="hljs-variable">$OUTPUT_DIR</span>&quot;</span><br><span class="hljs-built_in">mkdir</span> -p <span class="hljs-string">&quot;<span class="hljs-variable">$OUTPUT_DIR</span>&quot;</span><br><br><span class="hljs-comment"># 使用 appimagetool 创建 AppImage</span><br><span class="hljs-keyword">if</span> [ -f <span class="hljs-string">&quot;<span class="hljs-variable">$TOOLS_DIR</span>/appimagetool&quot;</span> ]; <span class="hljs-keyword">then</span><br>    <span class="hljs-string">&quot;<span class="hljs-variable">$TOOLS_DIR</span>/appimagetool&quot;</span> <span class="hljs-string">&quot;<span class="hljs-variable">$APPDIR_DIR</span>&quot;</span> <span class="hljs-string">&quot;<span class="hljs-variable">$OUTPUT_DIR</span>/simple-tool-<span class="hljs-variable">$APP_VERSION</span>-x86_64.AppImage&quot;</span><br><span class="hljs-keyword">else</span><br>    <span class="hljs-built_in">echo</span> <span class="hljs-string">&quot;Error: appimagetool not found. Please setup first.&quot;</span><br>    <span class="hljs-built_in">exit</span> 1<br><span class="hljs-keyword">fi</span><br><br><span class="hljs-built_in">echo</span> <span class="hljs-string">&quot;AppImage built successfully: <span class="hljs-variable">$OUTPUT_DIR</span>/simple-tool-<span class="hljs-variable">$APP_VERSION</span>-x86_64.AppImage&quot;</span> <br></code></pre></td></tr></table></figure><p>命令输出这样就是创建成功了：</p><p><img src="/img/build-jar-to-appimage/appimage-tool-output.png"></p><p>同时，我们的 output 目录下多出一个可执行文件：</p><p><img src="/img/build-jar-to-appimage/appimage-exec-file.png"></p><h1 id="FsXy2">4.测试程序</h1><p>现在，就可以像其他任何可执行文件一样，直接在命令行中执行这个文件了：</p><p><img src="/img/build-jar-to-appimage/appimage-execute-result.png"></p><p>到此大功告成，可以把这个文件分发到 x84_64 架构的 Linux 机器上执行了！</p><p>以上。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;如果你有一个 Jar 包，想要分发到服务器&amp;#x2F;客户&amp;#x2F;桌面等环境，但是又不想这些环境去配置 Java 环境该怎么办呢。本文章将介绍如何将一个 Jar 包打包成 Linux 的通用可执行文件：AppImage。&lt;/p&gt;
&lt;h1 id=&quot;dJQta&quot;&gt;1.App</summary>
      
    
    
    
    <category term="Computer Science" scheme="https://maphical.cn/categories/Computer-Science/"/>
    
    <category term="Java" scheme="https://maphical.cn/categories/Computer-Science/Java/"/>
    
    
  </entry>
  
  <entry>
    <title>Ant Design Pro 表格分页实现</title>
    <link href="https://maphical.cn/2021/09/ant-design-pro-form-page/"/>
    <id>https://maphical.cn/2021/09/ant-design-pro-form-page/</id>
    <published>2021-09-07T09:24:00.000Z</published>
    <updated>2025-10-11T16:10:29.448Z</updated>
    
    <content type="html"><![CDATA[<p>Ant Design Pro的表格分页方式有很多，这里介绍一种自己想到的实现。</p><p>在表格中，大量数据时需要进行分页显示，实现方法如下：</p><h2 id="1-改造接口支持分页"><a href="#1-改造接口支持分页" class="headerlink" title="1.改造接口支持分页"></a>1.改造接口支持分页</h2><p>将接口改造为支持分页的形式，一般是传入参数有current（当前页码）、pageSize（每页条目数），返回包括list（当页数据）和total（所有数据的总条目数）。</p><h2 id="2-设置页面状态（state）"><a href="#2-设置页面状态（state）" class="headerlink" title="2.设置页面状态（state）"></a>2.设置页面状态（state）</h2><p>实现分页需要记录当前页码和总条目数，在跳转分页时，通知组件进行渲染。实现如下：</p><h3 id="2-1-设置current、total、dataList和loading状态"><a href="#2-1-设置current、total、dataList和loading状态" class="headerlink" title="2.1 设置current、total、dataList和loading状态"></a>2.1 设置current、total、dataList和loading状态</h3><p>可以将pageSize固定在项目配置中，不动态调整。</p><p>使用useState设置以上状态，current初始化为1，total初始化为0，dataList设置为空数组，loading设置为false.代码：</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><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><code class="hljs typescript"><span class="hljs-keyword">const</span> [orderList, setOrderList] = useState&lt;<span class="hljs-variable constant_">APP_API</span>.<span class="hljs-property">Order</span>[]&gt;([])<br><span class="hljs-keyword">const</span> [current, setCurrent] = <span class="hljs-title function_">useState</span>(<span class="hljs-number">1</span>)<br><span class="hljs-keyword">const</span> [total, setTotal] = <span class="hljs-title function_">useState</span>(<span class="hljs-number">0</span>)<br><span class="hljs-keyword">const</span> [loading, setLoading] = <span class="hljs-title function_">useState</span>(<span class="hljs-literal">false</span>)<br></code></pre></td></tr></table></figure><h2 id="2-2-设置searchWord和dateRange状态（可选）"><a href="#2-2-设置searchWord和dateRange状态（可选）" class="headerlink" title="2.2 设置searchWord和dateRange状态（可选）"></a>2.2 设置searchWord和dateRange状态（可选）</h2><p>其中searchWord是搜索关键词，dateRange是日期搜索范围，设置目的是，在表格进行分页跳转时，重新渲染时保留筛选条件（调用接口筛选）。</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs typescript"><span class="hljs-keyword">const</span> [searchWord, setSearchWord] = <span class="hljs-title function_">useState</span>(<span class="hljs-string">&#x27;&#x27;</span>)<br><span class="hljs-keyword">const</span> [dateRange, setDateRange] = <span class="hljs-title function_">useState</span>(<span class="hljs-string">&#x27;&#x27;</span>)<br></code></pre></td></tr></table></figure><h2 id="3-编写加载数据的函数"><a href="#3-编写加载数据的函数" class="headerlink" title="3.编写加载数据的函数"></a>3.编写加载数据的函数</h2><p>编写根据当前页码和每页条目数加载更新数据的函数，用于分页跳转时的数据更新，以及首次加载时的数据加载。函数中需要包括更新状态的语句，用于更新页面的渲染，例：<br>​</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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><code class="hljs typescript"><span class="hljs-keyword">const</span> <span class="hljs-title function_">loadData</span> = (<span class="hljs-params"><span class="hljs-attr">current</span>: <span class="hljs-built_in">number</span></span>) =&gt; &#123;<br>    model.<span class="hljs-title function_">setLoading</span>(<span class="hljs-literal">true</span>)  <span class="hljs-comment">// 显示加载中状态</span><br>    <span class="hljs-title function_">getOrderList</span>(current, config.<span class="hljs-property">pageSize</span>).<span class="hljs-title function_">then</span>(<span class="hljs-function"><span class="hljs-params">response</span> =&gt;</span> &#123;<br>      <span class="hljs-keyword">if</span> (response.<span class="hljs-property">code</span> === <span class="hljs-number">200</span>) &#123; <span class="hljs-comment">// 拉取数据成功，更新数据</span><br>        model.<span class="hljs-title function_">setOrderList</span>(response.<span class="hljs-property">data</span>.<span class="hljs-property">list</span>)  <span class="hljs-comment">// 更新数据列表</span><br>        model.<span class="hljs-title function_">setTotal</span>(response.<span class="hljs-property">data</span>.<span class="hljs-property">total</span>)  <span class="hljs-comment">// 更新数据总条目数</span><br>        model.<span class="hljs-title function_">setCurrent</span>(current)  <span class="hljs-comment">// 更新当前页码</span><br>      &#125; <span class="hljs-keyword">else</span> &#123;<br>        message.<span class="hljs-title function_">error</span>(<span class="hljs-string">&#x27;拉取数据失败！&#x27;</span>)<br>      &#125;<br>      model.<span class="hljs-title function_">setLoading</span>(<span class="hljs-literal">false</span>)  <span class="hljs-comment">// 取消加载中状态</span><br>    &#125;)<br>  &#125;<br></code></pre></td></tr></table></figure><h2 id="4-设置表格分页"><a href="#4-设置表格分页" class="headerlink" title="4.设置表格分页"></a>4.设置表格分页</h2><p>设置表格的：</p><ul><li>数据源</li><li>加载中状态</li><li>分页设置<ul><li>默认页码</li><li>默认每页条目数</li><li>是否显示条目数修改器（当前逻辑中不显示）</li><li>分页切换回调</li></ul></li></ul><p>代码示例：</p><figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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><code class="hljs jsx">&lt;<span class="hljs-title class_">Table</span><br>      dataSource=&#123;model.<span class="hljs-property">driverList</span>&#125;  <span class="hljs-comment">// 数据源</span><br>      pagination=&#123;&#123;  <span class="hljs-comment">// 分页设置</span><br>        <span class="hljs-attr">defaultPageSize</span>: config.<span class="hljs-property">pageSize</span>,  <span class="hljs-comment">// 默认条目数</span><br>        <span class="hljs-attr">defaultCurrent</span>: model.<span class="hljs-property">current</span>,  <span class="hljs-comment">// 当前页码</span><br>        <span class="hljs-attr">showSizeChanger</span>: <span class="hljs-literal">false</span>,  <span class="hljs-comment">// 是否显示条目数修改器</span><br>        <span class="hljs-attr">onChange</span>: <span class="hljs-function">(<span class="hljs-params">current: number</span>) =&gt;</span> &#123;  <span class="hljs-comment">// 分页切换回调函数</span><br>          <span class="hljs-title function_">loadData</span>(current)<br>        &#125;<br>      &#125;&#125;<br>      loading=&#123;model.<span class="hljs-property">loading</span>&#125;  <span class="hljs-comment">// 表格加载状态</span><br>    &gt;<br>&lt;/<span class="hljs-title class_">Table</span>&gt;<br></code></pre></td></tr></table></figure><h2 id="5-设置页面数据首次初始化"><a href="#5-设置页面数据首次初始化" class="headerlink" title="5.设置页面数据首次初始化"></a>5.设置页面数据首次初始化</h2><p>进入页面时，根据当前状态刷新数据，进行数据的初始化。使用useEffect钩子进行loadData函数的调用：<br>​</p><figure class="highlight jsx"><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><code class="hljs jsx"><span class="hljs-title function_">useEffect</span>(<span class="hljs-function">() =&gt;</span> &#123;<br>    <span class="hljs-title function_">loadData</span>(model.<span class="hljs-property">current</span>)<br>  &#125;, [])  <span class="hljs-comment">// 空数组表示只调用一次</span><br></code></pre></td></tr></table></figure><h2 id="6-测试"><a href="#6-测试" class="headerlink" title="6.测试"></a>6.测试</h2><p>完成开发，进行测试。<br>​</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;Ant Design Pro的表格分页方式有很多，这里介绍一种自己想到的实现。&lt;/p&gt;
&lt;p&gt;在表格中，大量数据时需要进行分页显示，实现方法如下：&lt;/p&gt;
&lt;h2 id=&quot;1-改造接口支持分页&quot;&gt;&lt;a href=&quot;#1-改造接口支持分页&quot; class=&quot;headerlink</summary>
      
    
    
    
    <category term="Computer Science" scheme="https://maphical.cn/categories/Computer-Science/"/>
    
    <category term="Frameworks &amp; Tools" scheme="https://maphical.cn/categories/Computer-Science/Frameworks-Tools/"/>
    
    <category term="Ant Design Pro" scheme="https://maphical.cn/categories/Computer-Science/Frameworks-Tools/Ant-Design-Pro/"/>
    
    
  </entry>
  
  <entry>
    <title>Python多个装饰器的作用顺序</title>
    <link href="https://maphical.cn/2021/07/python-decorator-order/"/>
    <id>https://maphical.cn/2021/07/python-decorator-order/</id>
    <published>2021-07-26T07:31:20.000Z</published>
    <updated>2025-10-11T16:10:29.448Z</updated>
    
    <content type="html"><![CDATA[<p>如果一个函数应用了多个装饰器，那么这些装饰器被应用的先后顺序是：</p><ul><li>越靠近函数越被先调用，先被调用的装饰器的返回函数，会作为参数传递给后面调用的装饰器，即此装饰器上面的装饰器。</li></ul><p>实验代码：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">need1</span>(<span class="hljs-params">param: <span class="hljs-built_in">str</span></span>):<br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">decorate</span>(<span class="hljs-params">func</span>):<br>        <span class="hljs-keyword">def</span> <span class="hljs-title function_">inner</span>():<br>            <span class="hljs-keyword">return</span> func(param)<br>        <span class="hljs-keyword">return</span> inner<br>    <span class="hljs-keyword">return</span> decorate<br><br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">need2</span>(<span class="hljs-params">func</span>):<br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">inner</span>(<span class="hljs-params">param</span>):<br>        <span class="hljs-built_in">print</span>(param + <span class="hljs-string">&#x27;need2&#x27;</span>)<br>        <span class="hljs-keyword">return</span> func()<br>    <span class="hljs-keyword">return</span> inner<br><br><br><span class="hljs-meta">@need1(<span class="hljs-params"><span class="hljs-string">&#x27;need1&#x27;</span></span>)</span><br><span class="hljs-meta">@need2</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">fu</span>():<br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">&#x27;fu&#x27;</span>)<br><br><br>fu()<br></code></pre></td></tr></table></figure><p>运行输出为：</p><figure class="highlight nginx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs nginx"><span class="hljs-attribute">need1need2</span><br>fu<br></code></pre></td></tr></table></figure><p>其中need1装饰器需要一个具有一个参数的函数传入，need2装饰器需要一个无参函数，将两个装饰器按照上面顺序应用到一个无参函数上，可以被正确调用，说明need1正确接收到了具有一个参数的函数，即need2的返回值，证明首先调用了need2，得出越靠近函数的装饰器越被先调用。</p><p>若将两个装饰器位置调换，则输出为：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><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><code class="hljs vim">Traceback (most recent <span class="hljs-keyword">call</span> <span class="hljs-keyword">last</span>):<br>  File <span class="hljs-string">&quot;test.py&quot;</span>, <span class="hljs-built_in">line</span> <span class="hljs-number">21</span>, in <span class="hljs-symbol">&lt;module&gt;</span><br>    <span class="hljs-keyword">fu</span>()<br>TypeError: inner() missing <span class="hljs-number">1</span> required positional <span class="hljs-keyword">argument</span>: <span class="hljs-string">&#x27;param&#x27;</span><br></code></pre></td></tr></table></figure><p>inner被调用时缺少一个参数，即出错的位置在need2中，说明被传入need2的函数又一个参数，即need1的返回值被传入了need2中，首先调用了need1.</p><h2 id="注意"><a href="#注意" class="headerlink" title="注意"></a>注意</h2><p>这里的调用顺序是函数加载的时候的装饰器调用顺序，而不是这些装饰器生成函数后，各个装饰器的逻辑在生成的函数中被调用的顺序。</p><p>事实上各个装饰器的逻辑在被生成的函数中被调用的顺序是和这个顺序相反的，可以将目标函数想象成一个礼物，装饰器想象成一张张包装纸。在函数加载时最先被调用的装饰器，最为最内层包装被直接包装在礼物上，后面调用的装饰器按顺序被依次包装在上一层包装的外层，直到包装完毕。</p><p>而在装饰器生成的函数（包装好的礼物）被调用（拆开）的时候，是最外层的包装最先被撕掉，即最后被应用的装饰器中包含的逻辑最先被调用，再依次深入（拆开包装），直到目标函数（礼物）。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;如果一个函数应用了多个装饰器，那么这些装饰器被应用的先后顺序是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;越靠近函数越被先调用，先被调用的装饰器的返回函数，会作为参数传递给后面调用的装饰器，即此装饰器上面的装饰器。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;实验代码：&lt;/p&gt;
&lt;figure clas</summary>
      
    
    
    
    <category term="Computer Science" scheme="https://maphical.cn/categories/Computer-Science/"/>
    
    <category term="Programming Language" scheme="https://maphical.cn/categories/Computer-Science/Programming-Language/"/>
    
    <category term="Python" scheme="https://maphical.cn/categories/Computer-Science/Programming-Language/Python/"/>
    
    
  </entry>
  
  <entry>
    <title>深度学习简介</title>
    <link href="https://maphical.cn/2021/05/ai-intro/"/>
    <id>https://maphical.cn/2021/05/ai-intro/</id>
    <published>2021-05-09T08:31:00.000Z</published>
    <updated>2025-10-11T16:10:29.448Z</updated>
    
    <content type="html"><![CDATA[<h2 id="1-人工智能"><a href="#1-人工智能" class="headerlink" title="1.人工智能"></a>1.人工智能</h2><p>人工智能、机器学习和深度学习之间的关系，如下图：</p><p><img src="/img/AI-Intro-5.png" alt="1"></p><p>人工智能有多种实现形式，机器学习只是其中的一种。在机器学习之外，早期实现方式主要是专家系统——某一领域内的专家将解决问题的方法编码进系统中，代替专家对问题进行决策。这一时期的AI也称为「符号人工智能」。</p><h2 id="2-机器学习"><a href="#2-机器学习" class="headerlink" title="2.机器学习"></a>2.机器学习</h2><p>通常的编程工作是，开发者将处理数据的逻辑写入程序，使程序可以按照此逻辑对输入的数据进行处理，并输出开发者所希望的答案。</p><p>机器学习所做的是，开发者提供原始数据和答案，由机器去从输入数据和答案之间学习处理逻辑，学习到输入数据和答案之间的联系。使用一张图来表示为：</p><p><img src="/img/AI-Intro-1.png" alt="2"></p><p>我们使用输入数据和答案来训练计算机（模型），即下面的输入数据和期望输出数据。</p><h3 id="说明"><a href="#说明" class="headerlink" title="说明"></a><strong>说明</strong></h3><p>原始数据，可以理解为问题。</p><p>举个例子：我有华氏度和摄氏度的对应关系，但是我不知道他们的转换公式，我想通过机器学习的方式学习这个公式。那么，输入华氏度，计算机给出对应的摄氏度，则华氏度是输入数据，摄氏度是输出数据。</p><p>输出数据一般指计算机根据其学习情况，套用学习到的规律，依据输入数据得出的结果，即计算机计算得出的摄氏度。</p><p>期望的输出数据，是指正确答案，即正确的摄氏度。</p><h2 id="3-深度学习"><a href="#3-深度学习" class="headerlink" title="3.深度学习"></a>3.深度学习</h2><p>深度学习是一种从数据学习数据特征的数学框架，是机器学习的一种实现手段。深度学习的“深度”主要体现在框架的多层，每一层的输出作为下一层的输入。</p><p><img src="/img/AI-Intro-2.png" alt="3"></p><h2 id="4-神经网络"><a href="#4-神经网络" class="headerlink" title="4.神经网络"></a>4.神经网络</h2><p>神经网络是深度学习的主要方式，神经网络的定义，来自维基百科：</p><blockquote><p>人工神经网络（英语：Artificial Neural Network，ANN），简称神经网络（Neural Network，NN）或类神经网络，在机器学习和认知科学领域，是一种模仿生物神经网络（动物的中枢神经系统，特别是大脑）的结构和功能的数学模型或计算模型，用于对函数进行估计或近似。神经网络由大量的人工神经元联结进行计算。大多数情况下人工神经网络能在外界信息的基础上改变内部结构，是一种自适应系统，通俗地讲就是具备学习功能。现代神经网络是一种非线性统计性数据建模工具，神经网络通常是通过一个基于数学统计学类型的学习方法（Learning Method）得以优化，所以也是数学统计学方法的一种实际应用，通过统计学的标准数学方法我们能够得到大量的可以用函数来表达的局部结构空间，另一方面在人工智能学的人工感知领域，我们通过数学统计学的应用可以来做人工感知方面的决定问题（也就是说通过统计学的方法，人工神经网络能够类似人一样具有简单的决定能力和简单的判断能力），这种方法比起正式的逻辑学推理演算更具有优势。<br>和其他机器学习方法一样，神经网络已经被用于解决各种各样的问题，例如机器视觉和语音识别。这些问题都是很难被传统基于规则的编程所解决的。</p></blockquote><p>神经网络由不同数量的层组成，层由不同数量的神经元组成，神经元是一个线性函数：</p><p>y&#x3D;kx+b</p><p>其中 k 称为权重（weight），b 称为偏移（bias），这个函数构成一个神经元。</p><p>不同数量的神经元构成一个层（layer），而不同类型的神经元所组成的层，类型也不相同。如图。</p><p><img src="/img/AI-Intro-3.png" alt="4"></p><p>图中每一列代表一个层，相邻的层之间的神经元进行连接，数据从上一层传递到下一层。每一个圆点表示一个神经元，即一个线性函数。</p><h2 id="5-神经网络的学习过程"><a href="#5-神经网络的学习过程" class="headerlink" title="5.神经网络的学习过程"></a>5.神经网络的学习过程</h2><p>神经网络的学习过程就是：</p><blockquote><p>通过不断地输入数据，并根据输出结果，对网络中神经元中地参数值进行自动调节，使得输入数据通过网络后的输出，可以与期望的输出尽可能地相似。调节参数的过程，称为神经网络的学习。</p></blockquote><p>而这个学习过程是自动进行的，即，网络中参数的调节，是计算机自动完成的。大致基本过程为：</p><p><img src="/img/AI-Intro-4.png" alt="5"></p><p>图中：</p><ul><li>这是一个两层模型，输入层和输出层，没有中间层。</li><li>Input X为输入数据，输入数据进入第一层（线性函数中的 x），经过计算后的输出，作为输入进入第二层，作为第二层的 x，经过计算后输出为 y’ ，作为整个模型的输出（预测结果）。</li><li>y’ 与真实正确的数据 y 使用指定的损失函数进行比较，得出损失值（Loss score）。<ul><li><em>注：损失函数，用于衡量预测结果和真实结果之间差距的函数，不是某一个具体的函数，对于不同的问题，使用不同的损失函数。</em></li></ul></li><li>优化器使用损失值来决定如何对模型中的参数进行更新，更新的目标是：使得损失值尽可能得小<ul><li><em>注：优化器，用于根据损失值来决定如何更新模型参数得算法。不是某一个具体的算法，对不不同问题，使用不同的优化器，一般在使用时，指定学习率（Learning rate），确定每一次优化中的优化幅度。</em></li></ul></li><li>更新参数后，进行下一次循环，不断循环直到达到指定的循环次数，这个次数称作训练轮次（epoch）。</li></ul><h2 id="6-机器学习的常用工具"><a href="#6-机器学习的常用工具" class="headerlink" title="6.机器学习的常用工具"></a>6.机器学习的常用工具</h2><p><strong>机器学习框架：</strong></p><ul><li>Tensorflow（Google）</li><li>PyTorch（Facebook）</li></ul><p><strong>编程工具：</strong></p><ul><li>Anaconda（用于管理Python版本和工具包，数据科学的常用Python环境，集中管理虚拟环境）</li><li>JupyterLab（可交互式编程环境，可以分段运行代码，在代码中嵌入Markdown，实时显示代码运行结果，数据科学常用的工具，目前也被广泛运用在教育行业中）</li></ul><p><strong>常见依赖库：</strong></p><ul><li>CUDA（Nvidia提供的，让程序可以使用Nvidia显卡进行计算的库，AMD没有）</li><li>CuDNN（用于深度神经网络的GPU加速库）</li><li>NumPy（科学计算库，高性能矩阵运算）</li><li>Pandas（处理表格数据的常用工具库）</li><li>matplotlib（绘图工具库）</li></ul><p><strong>常见文件格式：</strong></p><ul><li>HDF5（用于保存大型数据集）</li><li>CSV</li><li>Excel</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;1-人工智能&quot;&gt;&lt;a href=&quot;#1-人工智能&quot; class=&quot;headerlink&quot; title=&quot;1.人工智能&quot;&gt;&lt;/a&gt;1.人工智能&lt;/h2&gt;&lt;p&gt;人工智能、机器学习和深度学习之间的关系，如下图：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/AI-Intr</summary>
      
    
    
    
    <category term="Computer Science" scheme="https://maphical.cn/categories/Computer-Science/"/>
    
    <category term="AI" scheme="https://maphical.cn/categories/Computer-Science/AI/"/>
    
    <category term="Deep Learning" scheme="https://maphical.cn/categories/Computer-Science/AI/Deep-Learning/"/>
    
    
  </entry>
  
  <entry>
    <title>「转」Python的GIL是什么鬼，多线程性能究竟如何</title>
    <link href="https://maphical.cn/2021/05/python-gil/"/>
    <id>https://maphical.cn/2021/05/python-gil/</id>
    <published>2021-05-02T05:03:00.000Z</published>
    <updated>2025-10-12T14:58:01.990Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>前言：博主在刚接触Python的时候时常听到GIL这个词，并且发现这个词经常和Python无法高效的实现多线程划上等号。本着不光要知其然，还要知其所以然的研究态度，博主搜集了各方面的资料，花了一周内几个小时的闲暇时间深入理解了下GIL，并归纳成此文，也希望读者能通过次本文更好且客观的理解GIL。</p></blockquote><blockquote><p>文章欢迎转载，但转载时请保留本段文字，并置于文章的顶部 作者：卢钧轶(cenalulu) 本文原文地址：<a href="/link/?t=joumkb-1680120196" target="_blank">http://cenalulu.github.io/python/gil-in-python/</a></p></blockquote><h2 id="GIL是什么"><a href="#GIL是什么" class="headerlink" title="GIL是什么"></a>GIL是什么</h2><p>首先需要明确的一点是 <code>GIL</code>并不是Python的特性，它是在实现Python解析器(CPython)时所引入的一个概念。就好比C++是一套语言（语法）标准，但是可以用不同的编译器来编译成可执行代码。有名的编译器例如GCC，INTEL C++，Visual C++等。Python也一样，同样一段代码可以通过CPython，PyPy，Psyco等不同的Python执行环境来执行。像其中的JPython就没有GIL。然而因为CPython是大部分环境下默认的Python执行环境。所以在很多人的概念里CPython就是Python，也就想当然的把 <code>GIL</code>归结为Python语言的缺陷。所以这里要先明确一点：GIL并不是Python的特性，Python完全可以不依赖于GIL</p><p>那么CPython实现中的GIL又是什么呢？GIL全称 <code>Global Interpreter Lock</code>为了避免误导，我们还是来看一下官方给出的解释：</p><blockquote><p>In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)</p></blockquote><p>好吧，是不是看上去很糟糕？一个防止多线程并发执行机器码的一个Mutex，乍一看就是个BUG般存在的全局锁嘛！别急，我们下面慢慢的分析。</p><h2 id="为什么会有GIL"><a href="#为什么会有GIL" class="headerlink" title="为什么会有GIL"></a>为什么会有GIL</h2><p>由于物理上的限制，各CPU厂商在核心频率上的比赛已经被多核所取代。为了更有效的利用多核处理器的性能，就出现了多线程的编程方式，而随之带来的就是线程间数据一致性和状态同步的困难。即使在CPU内部的Cache也不例外，为了有效解决多份缓存之间的数据同步时各厂商花费了不少心思，也不可避免的带来了一定的性能损失。</p><p>Python当然也逃不开，为了利用多核，Python开始支持多线程。<em>而解决多线程之间数据完整性和状态同步的最简单方法自然就是加锁。</em> 于是有了GIL这把超级大锁，而当越来越多的代码库开发者接受了这种设定后，他们开始大量依赖这种特性（即默认python内部对象是thread-safe的，无需在实现时考虑额外的内存锁和同步操作）。</p><p>慢慢的这种实现方式被发现是蛋疼且低效的。但当大家试图去拆分和去除GIL的时候，发现大量库代码开发者已经重度依赖GIL而非常难以去除了。有多难？做个类比，像MySQL这样的“小项目”为了把Buffer Pool Mutex这把大锁拆分成各个小锁也花了从5.5到5.6再到5.7多个大版为期近5年的时间，并且仍在继续。MySQL这个背后有公司支持且有固定开发团队的产品走的如此艰难，那又更何况Python这样核心开发和代码贡献者高度社区化的团队呢？</p><p>所以简单的说GIL的存在更多的是历史原因。如果推到重来，多线程的问题依然还是要面对，但是至少会比目前GIL这种方式会更优雅。</p><h2 id="GIL的影响"><a href="#GIL的影响" class="headerlink" title="GIL的影响"></a>GIL的影响</h2><p>从上文的介绍和官方的定义来看，GIL无疑就是一把全局排他锁。毫无疑问全局锁的存在会对多线程的效率有不小影响。甚至就几乎等于Python是个单线程的程序。 那么读者就会说了，全局锁只要释放的勤快效率也不会差啊。只要在进行耗时的IO操作的时候，能释放GIL，这样也还是可以提升运行效率的嘛。或者说再差也不会比单线程的效率差吧。理论上是这样，而实际上呢？Python比你想的更糟。</p><p>下面我们就对比下Python在多线程和单线程下得的效率对比。测试方法很简单，一个循环1亿次的计数器函数。一个通过单线程执行两次，一个多线程执行。最后比较执行总时间。测试环境为双核的Mac pro。注：为了减少线程库本身性能损耗对测试结果带来的影响，这里单线程的代码同样使用了线程。只是顺序的执行两次，模拟单线程。</p><h4 id="顺序执行的单线程-single-thread-py"><a href="#顺序执行的单线程-single-thread-py" class="headerlink" title="顺序执行的单线程(single_thread.py)"></a>顺序执行的单线程(single_thread.py)</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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><code class="hljs python"><span class="hljs-comment">#! /usr/bin/python</span><br><br><span class="hljs-keyword">from</span> threading <span class="hljs-keyword">import</span> Thread<br><span class="hljs-keyword">import</span> time<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">my_counter</span>():<br>    i = <span class="hljs-number">0</span><br>    <span class="hljs-keyword">for</span> _ <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(<span class="hljs-number">100000000</span>):<br>        i = i + <span class="hljs-number">1</span><br>    <span class="hljs-keyword">return</span> <span class="hljs-literal">True</span><br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">main</span>():<br>    thread_array = &#123;&#125;<br>    start_time = time.time()<br>    <span class="hljs-keyword">for</span> tid <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(<span class="hljs-number">2</span>):<br>        t = Thread(target=my_counter)<br>        t.start()<br>        t.join()<br>    end_time = time.time()<br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;Total time: &#123;&#125;&quot;</span>.<span class="hljs-built_in">format</span>(end_time - start_time))<br><br><span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">&#x27;__main__&#x27;</span>:<br>    main()<br></code></pre></td></tr></table></figure><h4 id="同时执行的两个并发线程-multi-thread-py"><a href="#同时执行的两个并发线程-multi-thread-py" class="headerlink" title="同时执行的两个并发线程(multi_thread.py)"></a>同时执行的两个并发线程(multi_thread.py)</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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><code class="hljs python"><span class="hljs-comment">#! /usr/bin/python</span><br><br><span class="hljs-keyword">from</span> threading <span class="hljs-keyword">import</span> Thread<br><span class="hljs-keyword">import</span> time<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">my_counter</span>():<br>    i = <span class="hljs-number">0</span><br>    <span class="hljs-keyword">for</span> _ <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(<span class="hljs-number">100000000</span>):<br>        i = i + <span class="hljs-number">1</span><br>    <span class="hljs-keyword">return</span> <span class="hljs-literal">True</span><br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">main</span>():<br>    thread_array = &#123;&#125;<br>    start_time = time.time()<br>    <span class="hljs-keyword">for</span> tid <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(<span class="hljs-number">2</span>):<br>        t = Thread(target=my_counter)<br>        t.start()<br>        thread_array[tid] = t<br>    <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(<span class="hljs-number">2</span>):<br>        thread_array[i].join()<br>    end_time = time.time()<br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;Total time: &#123;&#125;&quot;</span>.<span class="hljs-built_in">format</span>(end_time - start_time))<br><br><span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">&#x27;__main__&#x27;</span>:<br>    main()<br></code></pre></td></tr></table></figure><p>下图就是测试结果</p><p><img src="/img/python-gil/test_result.jpg" alt="测试结果一"></p><p>可以看到python在多线程的情况下居然比单线程整整慢了45%。按照之前的分析，即使是有GIL全局锁的存在，串行化的多线程也应该和单线程有一样的效率才对。那么怎么会有这么糟糕的结果呢？</p><p>让我们通过GIL的实现原理来分析这其中的原因。</p><h2 id="当前GIL设计的缺陷"><a href="#当前GIL设计的缺陷" class="headerlink" title="当前GIL设计的缺陷"></a>当前GIL设计的缺陷</h2><h3 id="基于pcode数量的调度方式"><a href="#基于pcode数量的调度方式" class="headerlink" title="基于pcode数量的调度方式"></a>基于pcode数量的调度方式</h3><p>按照Python社区的想法，操作系统本身的线程调度已经非常成熟稳定了，没有必要自己搞一套。所以Python的线程就是C语言的一个pthread，并通过操作系统调度算法进行调度（例如linux是CFS）。为了让各个线程能够平均利用CPU时间，python会计算当前已执行的微代码数量，达到一定阈值后就强制释放GIL。而这时也会触发一次操作系统的线程调度（当然是否真正进行上下文切换由操作系统自主决定）。</p><p>伪代码</p><figure class="highlight arcade"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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><code class="hljs arcade"><span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:<br>    acquire GIL<br>    <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-number">1000</span>:<br>        <span class="hljs-keyword">do</span> something<br>    release GIL<br>    <span class="hljs-comment">/* Give Operating System a chance to do thread scheduling */</span><br></code></pre></td></tr></table></figure><p>这种模式在只有一个CPU核心的情况下毫无问题。任何一个线程被唤起时都能成功获得到GIL（因为只有释放了GIL才会引发线程调度）。但当CPU有多个核心的时候，问题就来了。从伪代码可以看到，从 <code>release GIL</code>到 <code>acquire GIL</code>之间几乎是没有间隙的。所以当其他在其他核心上的线程被唤醒时，大部分情况下主线程已经又再一次获取到GIL了。这个时候被唤醒执行的线程只能白白的浪费CPU时间，看着另一个线程拿着GIL欢快的执行着。然后达到切换时间后进入待调度状态，再被唤醒，再等待，以此往复恶性循环。</p><p>PS：当然这种实现方式是原始而丑陋的，Python的每个版本中也在逐渐改进GIL和线程调度之间的互动关系。例如先尝试持有GIL在做线程上下文切换，在IO等待时释放GIL等尝试。但是无法改变的是GIL的存在使得操作系统线程调度的这个本来就昂贵的操作变得更奢侈了。 <a href="/link/?t=OAy2SO-1680121023" target="_blank">关于GIL影响的扩展阅读</a></p><p>为了直观的理解GIL对于多线程带来的性能影响，这里直接借用的一张测试结果图（见下图）。图中表示的是两个线程在双核CPU上得执行情况。两个线程均为CPU密集型运算线程。绿色部分表示该线程在运行，且在执行有用的计算，红色部分为线程被调度唤醒，但是无法获取GIL导致无法进行有效运算等待的时间。 </p><p><img src="/img/python-gil/GIL_2cpu.png" alt="GIL Performance"></p><p>由图可见，GIL的存在导致多线程无法很好的立即多核CPU的并发处理能力。</p><p>那么Python的IO密集型线程能否从多线程中受益呢？我们来看下面这张测试结果。颜色代表的含义和上图一致。白色部分表示IO线程处于等待。可见，当IO线程收到数据包引起终端切换后，仍然由于一个CPU密集型线程的存在，导致无法获取GIL锁，从而进行无尽的循环等待。 </p><p><img src="/img/python-gil/GIL_ioclose.png" alt="GIL IO Performance"></p><p>简单的总结下就是：Python的多线程在多核CPU上，只对于IO密集型计算产生正面效果；而当有至少有一个CPU密集型线程存在，那么多线程效率会由于GIL而大幅下降。</p><h2 id="如何避免受到GIL的影响"><a href="#如何避免受到GIL的影响" class="headerlink" title="如何避免受到GIL的影响"></a>如何避免受到GIL的影响</h2><p>说了那么多，如果不说解决方案就仅仅是个科普帖，然并卵。GIL这么烂，有没有办法绕过呢？我们来看看有哪些现成的方案。</p><h3 id="用multiprocessing替代Thread"><a href="#用multiprocessing替代Thread" class="headerlink" title="用multiprocessing替代Thread"></a>用multiprocessing替代Thread</h3><p>multiprocessing库的出现很大程度上是为了弥补thread库因为GIL而低效的缺陷。它完整的复制了一套thread所提供的接口方便迁移。唯一的不同就是它使用了多进程而不是多线程。每个进程有自己的独立的GIL，因此也不会出现进程之间的GIL争抢。</p><p>当然multiprocessing也不是万能良药。它的引入会增加程序实现时线程间数据通讯和同步的困难。就拿计数器来举例子，如果我们要多个线程累加同一个变量，对于thread来说，申明一个global变量，用thread.Lock的context包裹住三行就搞定了。而multiprocessing由于进程之间无法看到对方的数据，只能通过在主线程申明一个Queue，put再get或者用share memory的方法。这个额外的实现成本使得本来就非常痛苦的多线程程序编码，变得更加痛苦了。</p><h3 id="用其他解析器"><a href="#用其他解析器" class="headerlink" title="用其他解析器"></a>用其他解析器</h3><p>之前也提到了既然GIL只是CPython的产物，那么其他解析器是不是更好呢？没错，像JPython和IronPython这样的解析器由于实现语言的特性，他们不需要GIL的帮助。然而由于用了Java&#x2F;C#用于解析器实现，他们也失去了利用社区众多C语言模块有用特性的机会。所以这些解析器也因此一直都比较小众。毕竟功能和性能大家在初期都会选择前者，<code>Done is better than perfect</code>。</p><h3 id="所以没救了么？"><a href="#所以没救了么？" class="headerlink" title="所以没救了么？"></a>所以没救了么？</h3><p>当然Python社区也在非常努力的不断改进GIL，甚至是尝试去除GIL。并在各个小版本中有了不少的进步。有兴趣的读者可以扩展阅读<a href="/link/?t=G59jEF-1680121094" target="_blank">这个Slide</a> 另一个改进 <a href="/link/?t=OmBn77-1680121139" target="_blank">Reworking the GIL</a></p><ul><li>将切换颗粒度从基于opcode计数改成基于时间片计数</li><li>避免最近一次释放GIL锁的线程再次被立即调度</li><li>新增线程优先级功能（高优先级线程可以迫使其他线程释放所持有的GIL锁）</li></ul><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>Python GIL其实是功能和性能之间权衡后的产物，它尤其存在的合理性，也有较难改变的客观因素。从本分的分析中，我们可以做以下一些简单的总结：</p><ul><li>因为GIL的存在，只有IO Bound场景下得多线程会得到较好的性能</li><li>如果对并行计算性能较高的程序可以考虑把核心部分也成C模块，或者索性用其他语言实现</li><li>GIL在较长一段时间内将会继续存在，但是会不断对其进行改进</li></ul><h2 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h2><p><a href="/link/?t=2zfP0f-1680121254" target="_blank">1. Python&#39;s hardest problem</a></p><p><a href="/link/?t=yiiw2k-1680121300" target="_blank">2. Official documents about GIL</a></p><p><a href="/link/?t=XcYp53-1680121358" target="_blank">3. Revisiting thread priorities and the new GIL</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;前言：博主在刚接触Python的时候时常听到GIL这个词，并且发现这个词经常和Python无法高效的实现多线程划上等号。本着不光要知其然，还要知其所以然的研究态度，博主搜集了各方面的资料，花了一周内几个小时的闲暇时间深入理解了下GIL，并归纳成此文</summary>
      
    
    
    
    <category term="Computer Science" scheme="https://maphical.cn/categories/Computer-Science/"/>
    
    <category term="Programming Language" scheme="https://maphical.cn/categories/Computer-Science/Programming-Language/"/>
    
    <category term="Python" scheme="https://maphical.cn/categories/Computer-Science/Programming-Language/Python/"/>
    
    
  </entry>
  
  <entry>
    <title>我为什么从事计算机行业</title>
    <link href="https://maphical.cn/2020/12/reason-i-worked-as-an-it-man/"/>
    <id>https://maphical.cn/2020/12/reason-i-worked-as-an-it-man/</id>
    <published>2020-12-09T15:24:00.000Z</published>
    <updated>2025-10-11T16:10:29.448Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>只有你认为你正在做的事情是有意义的，你才会更加投入和具有热情，否则，我为什么要消耗我的生命去做一件无意义的事？有人会说，因为你需要生存，需要养家糊口。但是对于当前的选择，一定是有理由的，既然你可以从事计算机行业，那么你一定也可以从事其他能够“养家糊口”让自己不饿肚子的行业，这已经是工作的最低标准。</p></blockquote><p>如今，计算机行业已经趋于成熟。并且经过21世纪以来的高速发展，计算机行业也在大多数人的认识里和高薪画上了等号，有越来越多的人转行进入计算机和互联网行业中，通过一段时间的培训，或者是自学，就可以有一份不错的薪资。当然，收入水平是衡量一个行业吸引力的重要指标，但并不是唯一指标。</p><p>计算机行业又分为两大领域：硬件和软件。但无论是哪个领域，或者大领域中的细分领域，都是未来数字世界的基础设施，而我们就像15年前的建筑工人一样，为了现在和将来的数字高楼大厦和繁华市景，进行着建筑工作。</p><p>但当我们每日进行着CRUD的时候，是否有想过我们所做的事情，到底有什么样的意义。肯定有人会说：“我才不会去思考这些虚无缥缈的东西，只需要有不错的薪资，让我过上满意的生活就可以了”。这种想法当然没错，人生在世，首先要解决的是衣食住行，对于「意义」什么的，这种哲学上的东西，一般也都是在解决了基本需求后才会思考的，仓廪实而知礼节。对于我来说，也是遵循同样的逻辑，但是一份有意义的工作，会让我更加充满动力。</p><p>对于我来说，从事计算机行业并不是一朝一夕就决定的事情，也并不完全因为相对于传统行业的高薪，我从高中就开始自学编程也许能说明这一点。从高一开始，我对计算机的兴趣渐渐浮上表面，如果说之前也对电脑感兴趣，那完全是因为想要玩游戏，大多数的男孩子都有的兴趣，而高一开始，我则对电脑的运行方式产生了兴趣，看着一行行在漆黑屏幕上的滚动的代码（当时大部分是科幻电影里出现的），就会想要去搞懂这些字符的意思。</p><p>由于这种想要在自己电脑上“滚屏”的冲动，让我开始接触到Linux——使用起来最像黑客的操作系统，并开始不停地往电脑上安装不同发行版的Linux，Ubuntu、CentOS、Kali Linux、Fedora等，在和这些发行版玩耍时，为了便于操作，我在网上买了一本Linux Shell脚本编程详解，把它带到学校，课间就会拿出来翻看并在笔记本上做笔记，搞得比上课还认真……</p><p>高一高二两年是我对编程浅尝辄止的两年，也算是野生入门了吧，现在想想如果你问我我学习的第一门编程语言是什么，我会说是Bash Shell，很奇怪吧，大部分人应该都是C语言或者Python啊……顺便一讲，我的语言学习路径是：Bash、Visual Basic、JavaScript、PHP、C、C++、Java、C#、Python、Scala，目前主力语言是Java、Python和Scala，分别对应三个用途：CRUD业务逻辑、AI（或OA）和大数据。</p><p>如果说高一高二是由于喜欢酷酷的感觉而接触计算机和编程，那么高三时我的理由变得更“宏大”了。</p><p>从小，看着美式科幻电影长大的我，对宇宙充满了兴趣，倒不是对外星人什么的兴趣（虽然也有一些），而是对于人类探索宇宙的期待。比如人类在外星球上的基地应该是什么样子的，有哪些功能，飞船使用什么动力，在宇宙里用什么武器比较有效等等。</p><p>而随着自然科学知识的增加（理科生），我渐渐认识到，宇宙这么大，地球这么小，人类这个物种最终一定会走出地球进入宇宙，而宇宙中空间大到难以想象，光是同一星系不同的星球之间就有着在地球上无法认识的距离，更不用说飞出太阳系之后了。而我们想要探索宇宙，到后来的定居其他星球，实现这些目标，就需要星际航行。鉴于外太空纷繁复杂的空间环境，例如引力影响、小行星撞击、射线危害，以及轨道的计算、应急情况的快速处理、行动的决策等，单凭人类大脑的认知能力和信息处理能力，完全没有可能达到要求的合理的速度和正确性，不说这些，单独使用大脑来进行工业生产，人类甚至无法制造出能够星际航行的宇宙飞船……（也许能否做到正确调度火车班次都是个问题，更不用说可控核聚变等能源要求了）</p><p>所以，对人类发展来说，我们需要一个智能——第二大脑，也就是计算机，来帮助我们在另一个数量级的速度上处理信息、做出决策以及精密控制，这不光是决定我们现在能否在家里不出门叫到外卖的问题，而是关乎人类未来的前途以及人类文明的能力问题。</p><p>举个例子，在不使用计算机的情况下，如何控制一座大型核电站的正常运转以及危机处理？可以让好多位操作员24小时轮流排班盯着数万个仪表板，将他们编为多个小组，当某些仪表出现异常读数时，最快速度通过小组通知所有操作员，需要进行处理操作的操作员们按照规程进行处理，处理结束后反馈给异常发出处的操作员小组，确认异常得到解除。若未解除，则需要所有操作员层层上报仪表数据，汇总到某个核电专家组处，专家组通过一本厚厚的仪表参数报表分析研判问题所在，将解决方法层层下放到各个操作小组，由操作员进行调整，问题得以解决。而这一决策过程可能需要半个小时，且不保证不会出现操作员误操作和数据误报的情况出现，专家组和操作员需要24小时轮班来确保核电站正常运行，哦对了，半小时后可能异常已经变化了，需要重新研判……或者，马上撤离……</p><p>毫无疑问，计算机的出现和大规模应用，使得人类在文明维度上，有了一个巨大的进步和更大的未来可能性。这只是在基于计算机没有智能的前提下得出的结论，而人工智能的出现，对于我们来说，意义和计算机被发明同样重大，甚至更加。</p><p>而这一切，包括AI出现后可能的其他相关跨越式发明和发现，都是以计算机的发展为基础的，那么计算机对于人类文明的重要性不言而喻。</p><p>甚至可以毫不夸张地说，在计算机出现之前和之后，人类文明是两个样貌截然不同的文明，它是文明发展的加速器和更高级文明的加载器，如果说文明在宇宙中是要被考验的话，计算机则大大增加了人类通过“大过滤器”的可能性（大过滤器理论：费米悖论的一种解释）。</p><p>科技是一把双刃剑，对于计算机这一工具的不同利用方式会造成截然不同的结果（有益或是有害），那么作为人类操控这一极具意义工具的操作员的我们（计算机行业从业者）来说，如何利用计算机为人类创造福祉、为文明开辟道路，这是值得我们每个人去思考的问题。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;只有你认为你正在做的事情是有意义的，你才会更加投入和具有热情，否则，我为什么要消耗我的生命去做一件无意义的事？有人会说，因为你需要生存，需要养家糊口。但是对于当前的选择，一定是有理由的，既然你可以从事计算机行业，那么你一定也可以从事其他能够“养家糊</summary>
      
    
    
    
    <category term="Computer Science" scheme="https://maphical.cn/categories/Computer-Science/"/>
    
    <category term="Views" scheme="https://maphical.cn/categories/Computer-Science/Views/"/>
    
    
  </entry>
  
  <entry>
    <title>Scala基础语法中的关键点</title>
    <link href="https://maphical.cn/2020/09/scala-basic-key-points/"/>
    <id>https://maphical.cn/2020/09/scala-basic-key-points/</id>
    <published>2020-09-02T02:15:00.000Z</published>
    <updated>2025-10-11T16:10:29.448Z</updated>
    
    <content type="html"><![CDATA[<p>由于工作的需要，最近开始学习Scala和Flink，下面是学习途中做的笔记，略作了修改，希望能够给正在学习Scala的朋友一些帮助。</p><h2 id="一、变量的定义"><a href="#一、变量的定义" class="headerlink" title="一、变量的定义"></a>一、变量的定义</h2><p>Scala变量使用两种方式定义：</p><ul><li><p><strong>var</strong></p><ul><li>使用<code>var</code>定义的变量的值可以被修改。</li></ul><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs scala"><span class="hljs-keyword">var</span> a: <span class="hljs-type">String</span> = <span class="hljs-string">&quot;MaphicalYng&quot;</span>  <span class="hljs-comment">// 格式：var &lt;变量名&gt;: &lt;变量类型&gt; = &lt;变量值&gt;</span><br></code></pre></td></tr></table></figure></li><li><p><strong>val</strong></p><ul><li>使用<code>val</code>定义的变量不能被修改。因为在日常对象使用中，我们很少去修改对象引用本身，而只是修改对象的属性。其次，<code>val</code>是线程安全的，Scala设计者推荐使用<code>val</code>。</li></ul><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs scala"><span class="hljs-keyword">val</span> a: <span class="hljs-type">String</span> = <span class="hljs-string">&quot;MaphicalYng&quot;</span>  <span class="hljs-comment">// 格式：val &lt;变量名&gt;: &lt;变量类型&gt; = &lt;变量值&gt;</span><br></code></pre></td></tr></table></figure></li></ul><h2 id="二、数据类型"><a href="#二、数据类型" class="headerlink" title="二、数据类型"></a>二、数据类型</h2><p>所有的类型都是对象，没有基本数据类型。</p><p>例如：</p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs scala"><span class="hljs-number">1.</span>toString  <span class="hljs-comment">// 等同于 1.toString()</span><br></code></pre></td></tr></table></figure><p>这里<code>toString</code>是一个方法，在Scala中，没有形参的方法可以不加括号进行调用。可以对1进行方法调用，说明1在Scala中是一个对象。</p><p><strong>注意点：</strong></p><ul><li>数据类型可以分为两大类，值类型和引用类型，分别是下面两个类的子类：<ul><li><code>AnyVal</code>：值类型的父类</li><li><code>AnyRef</code>：引用类型的父类</li></ul></li><li>有一个所有类的父类：根类——<code>Any</code>。</li></ul><h3 id="1-默认类型"><a href="#1-默认类型" class="headerlink" title="1.默认类型"></a>1.默认类型</h3><p>整数类型默认为<code>Int</code>类型，浮点型默认为<code>Double</code>类型。Scala会自动将低精度转换为高精度，例如<code>Float</code>类型的值可以赋给<code>Double</code>类型的值，但不能相反。</p><h3 id="2-浮点数精度"><a href="#2-浮点数精度" class="headerlink" title="2.浮点数精度"></a>2.浮点数精度</h3><p><code>Float</code>精度为点后6位，多位会丢失，一般使用<code>Double</code>因为精度高。</p><h2 id="三、数据类型体系图"><a href="#三、数据类型体系图" class="headerlink" title="三、数据类型体系图"></a>三、数据类型体系图</h2><p><img src="/img/scala-lang-data-type-class-hierarchy.png" alt="AnyVaI  Double  Long  Int  Short  Byte  unit  StringOps  (Scala collections)  AnyRef  (Other Scala classes)  (all java classes)  Null  Nothing  Subtype  Implicit Conversion  Class Hierarchy "></p><h2 id="四、字符类型"><a href="#四、字符类型" class="headerlink" title="四、字符类型"></a>四、字符类型</h2><h3 id="1-Char类型"><a href="#1-Char类型" class="headerlink" title="1.Char类型"></a>1.Char类型</h3><p>Char类型为一个Unicode字符，可以使用正数表示，范围在U+0000 - U+FFFF。可以当作整数进行运算。</p><h3 id="2-赋值逻辑"><a href="#2-赋值逻辑" class="headerlink" title="2.赋值逻辑"></a>2.赋值逻辑</h3><p>将一个表达式和一个常量赋值给一个变量时，逻辑不同：</p><ul><li><p>表达式：</p><ul><li>编译器在将表达式给变量赋值时，会先进行表达式的计算，此时会进行两个工作：（1）类型转换；（2）范围判断。所以当表达式的值为一个高精度类型时，就不能将此表达式的值赋值给一个低精度类型，典型例子：</li></ul><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs scala"><span class="hljs-keyword">var</span> a: <span class="hljs-type">Char</span> = <span class="hljs-number">97</span> + <span class="hljs-number">1</span> <span class="hljs-comment">// 报错  </span><br></code></pre></td></tr></table></figure></li><li><p>常量：</p><ul><li>编译器将常量给变量赋值时，只会进行范围的判断，即常量值在变量类型允许的范围中即可，而不会进行类型的判断，此时可以将高精度的常量赋值给低精度类型的变量，典型例子：</li></ul><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs scala"><span class="hljs-keyword">var</span> a: <span class="hljs-type">Char</span> = <span class="hljs-number">98</span> <span class="hljs-comment">// 正常运行  </span><br></code></pre></td></tr></table></figure></li></ul><h2 id="五、Unit类型、Null类型和Nothing类型"><a href="#五、Unit类型、Null类型和Nothing类型" class="headerlink" title="五、Unit类型、Null类型和Nothing类型"></a>五、Unit类型、Null类型和Nothing类型</h2><ul><li><p><code>Unit</code>类型相当于Java中的<code>void</code>，表示空值，此类型只有一个值：<code>()</code></p></li><li><p><code>Null</code>类型是所有<code>AnyRef</code>类型的子类，只有一个值：<code>null</code>。可以赋值给任意引用类型（<code>AnyRef</code>），但不能赋值给值类型（<code>AnyVal</code>）。</p></li><li><p><code>Nothing</code>类型是所有类型的子类，可以赋值给所类型（<code>Any</code>），开发中可以将<code>Nothing</code>返回给任何函数调用或类型变量。</p></li></ul><h2 id="六、变量名称的格式"><a href="#六、变量名称的格式" class="headerlink" title="六、变量名称的格式"></a>六、变量名称的格式</h2><p>和Java的变量命名风格基本相同，其中不同的是：</p><ul><li>首字符可以是操作符（+-*&#x2F;），但若首字母是操作符，则后续必须也是操作符，且至少跟1个，操作符不能出现在变量名的中间或最后。</li><li>关键字也可以作为变量名，例如true，使用反引号（&#96;&#96;）包括起来即可。</li></ul><h2 id="七、变量支持代码块赋值"><a href="#七、变量支持代码块赋值" class="headerlink" title="七、变量支持代码块赋值"></a>七、变量支持代码块赋值</h2><p>在给变量赋值时，可以使用一个大括号将一些代码括住，这些代码的执行结果作为变量的值赋给变量，例：</p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs scala"><span class="hljs-keyword">var</span> a1: <span class="hljs-type">Int</span> = &#123; <span class="hljs-number">1</span> + <span class="hljs-number">1</span> &#125;<br></code></pre></td></tr></table></figure><h2 id="八、表达式的值"><a href="#八、表达式的值" class="headerlink" title="八、表达式的值"></a>八、表达式的值</h2><p>Scala中任意表达式都有返回值，表达式可以是一个代码块，使用大括号括住，具体的值取决于表达式代码块的最后一行内容。</p><h2 id="九、for循环"><a href="#九、for循环" class="headerlink" title="九、for循环"></a>九、for循环</h2><p>Scala中的循环和Java中有较大差异，Scala中有3种for循环：前后范围闭合的循环、前闭合后开放的循环、对可遍历对象的循环。</p><p><strong>（1）前后范围闭合的循环</strong></p><figure class="highlight scala"><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><code class="hljs scala"><span class="hljs-keyword">for</span> (i &lt;- start to end) &#123;<br>  println(i) <br>&#125;<br></code></pre></td></tr></table></figure><p>此种方式会将start和end都包括进去，打印值为start到end。</p><p><strong>（2）前闭合后开放的循环</strong></p><figure class="highlight scala"><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><code class="hljs scala"><span class="hljs-keyword">for</span> (i &lt;- start until end) &#123;<br>  println(i) <br>&#125;<br></code></pre></td></tr></table></figure><p>此种方式会将start包括进去，而不会包括end，打印值为start到end-1。数据范围部分也可以使用<code>Range(start, end)</code>替换。</p><p><strong>（3）对可遍历对象的循环</strong></p><figure class="highlight scala"><table><tr><td class="gutter"><pre><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><code class="hljs scala"><span class="hljs-keyword">val</span> list: <span class="hljs-type">List</span>[<span class="hljs-type">Int</span>] = <span class="hljs-type">List</span>(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>) <br><span class="hljs-keyword">for</span> (i &lt;- list) &#123;<br>  println(i)<br>&#125;<br></code></pre></td></tr></table></figure><p>此种方式会将list的值遍历打印出来。</p><h3 id="1-循环守卫"><a href="#1-循环守卫" class="headerlink" title="1.循环守卫"></a>1.循环守卫</h3><p>循环守卫是一句在for循环中的判断语句，当此语句判断为<code>true</code>时，会进入循环体中执行，若判断为<code>false</code>，会跳过这次循环，进入下一次循环。</p><figure class="highlight scala"><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><code class="hljs scala"><span class="hljs-keyword">for</span> (i &lt;- start to end <span class="hljs-keyword">if</span> i != <span class="hljs-number">2</span>) &#123;<br>  println(i)<br>&#125;<br></code></pre></td></tr></table></figure><p>此处打印的值为（start&#x3D;1，end&#x3D;3）：1，3，跳过了2. </p><h3 id="2-引入变量"><a href="#2-引入变量" class="headerlink" title="2.引入变量"></a>2.引入变量</h3><p>可以在循环头部语句中加入变量，例：</p><figure class="highlight scala"><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><code class="hljs scala"><span class="hljs-keyword">for</span> (i &lt;- start to end; j = <span class="hljs-number">5</span> - i) &#123;<br>  println(j)<br>&#125;<br></code></pre></td></tr></table></figure><p>次数会引入变量j，打印结果为（start&#x3D;1，end&#x3D;3）：4，3，2. </p><h3 id="3-嵌套循环"><a href="#3-嵌套循环" class="headerlink" title="3.嵌套循环"></a>3.嵌套循环</h3><p>在循环头部写入嵌套循环，例：</p><figure class="highlight scala"><table><tr><td class="gutter"><pre><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><code class="hljs scala"><span class="hljs-keyword">for</span> (i &lt;- start to end; j &lt;- i to end) &#123;<br>  println(i)<br>  println(j)<br>&#125;<br></code></pre></td></tr></table></figure><p>相当于等价代码：</p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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><code class="hljs scala"><span class="hljs-keyword">for</span> (i &lt;- start to end) &#123;<br>  <span class="hljs-keyword">for</span> (j  &lt;- i to end) &#123;<br>    println(i)<br>    println(j)<br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="4-返回值"><a href="#4-返回值" class="headerlink" title="4.返回值"></a>4.返回值</h3><p>for循环可以有返回值，使用<code>yield</code>关键字可以将返回值存储到一个<code>Vector</code>集合中。一个常用应用方式是，将一堆数据经过业务逻辑处理之后形成新的数据集合，或者进行多次处理形成集合，示例代码：</p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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><code class="hljs scala"><span class="hljs-keyword">val</span> list1: <span class="hljs-type">List</span>[<span class="hljs-type">Int</span>] = <span class="hljs-type">List</span>(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">6</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>)<br><span class="hljs-keyword">val</span> res = <span class="hljs-keyword">for</span> (i &lt;- list1) <span class="hljs-keyword">yield</span> &#123;<br>  <span class="hljs-keyword">if</span> (i % <span class="hljs-number">2</span> == <span class="hljs-number">0</span>) &#123;<br>    i * <span class="hljs-number">2</span><br>  &#125; <span class="hljs-keyword">else</span> &#123;<br>    i * <span class="hljs-number">3</span><br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>上述代码功能为：将<code>list1</code>列表中的所有偶数扩大2倍，奇数扩大3倍，并存储在新的集合中。</p><p><em>注：for的小括号可以换为大括号并换行写条件。</em></p><h2 id="十、break和continue"><a href="#十、break和continue" class="headerlink" title="十、break和continue"></a>十、break和continue</h2><p>Scala中没有<code>break</code>和<code>continue</code>关键字，而是使用其他方式替代其功能。</p><ol><li><strong>break</strong></li></ol><p>使用函数<code>util.control.Breaks.break</code>以及高阶函数<code>util.control.Breaks.breakable</code>来实现<code>break</code>关键字的功能，示例代码：</p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs scala">breakable&#123;<br>  <span class="hljs-keyword">for</span> (i &lt;- <span class="hljs-type">Range</span>(<span class="hljs-number">1</span>, <span class="hljs-number">20</span>)) &#123;<br>    <span class="hljs-keyword">if</span> (i == <span class="hljs-number">6</span>) &#123;<br>      <span class="hljs-keyword">break</span>()<br>    &#125;<br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p><code>break()</code>函数会抛出一个<code>breakException</code>，而高阶函数<code>breakable</code>函数会catch这个异常，实现跳出循环的效果。</p><ol start="2"><li><strong>continue</strong></li></ol><p>使用循环守卫或者循环体内的if判断来实现<code>continue</code>的效果，示例代码：</p><figure class="highlight scala"><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><code class="hljs scala"><span class="hljs-keyword">for</span> (i &lt;- <span class="hljs-type">Range</span>(<span class="hljs-number">1</span>, <span class="hljs-number">20</span>) <span class="hljs-keyword">if</span> i != <span class="hljs-number">14</span>) &#123;<br>  println(i)<br>&#125;<br></code></pre></td></tr></table></figure><p>上述代码表示当<code>i</code>不等于14时进入循环体，实现了当<code>i</code>等于14时执行<code>continue</code>的效果。</p><p>第二种方式是使用循环体内部的if判断，实例代码为：</p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs scala"><span class="hljs-keyword">for</span> (i &lt;- <span class="hljs-type">Range</span>(<span class="hljs-number">1</span>, <span class="hljs-number">20</span>)) &#123;<br>  <span class="hljs-keyword">if</span> (i != <span class="hljs-number">14</span>) &#123;<br>    println(i)<br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure><h2 id="十一、函数的定义"><a href="#十一、函数的定义" class="headerlink" title="十一、函数的定义"></a>十一、函数的定义</h2><p><strong>基本语法：</strong></p><pre>def 函数名([参数名: 参数类型, …])[[: 返回值类型] =] {  语句…  return 返回值}</pre><p>关于返回值：</p><ul><li>: 返回值类型 &#x3D; <ul><li>表示有返回值，且返回值类型确定</li></ul></li><li>&#x3D;<ul><li>表示返回值类型不确定，需要使用类型推导</li></ul></li><li>（空）<ul><li>表示没有返回值，<code>return</code>无效</li></ul></li></ul><h3 id="1-函数定义位置"><a href="#1-函数定义位置" class="headerlink" title="1.函数定义位置"></a>1.函数定义位置</h3><p>函数定义位置不影响其作用域，在方法中定义方法，可以和其他方法同等使用。</p><h3 id="2-函数参数的默认值"><a href="#2-函数参数的默认值" class="headerlink" title="2.函数参数的默认值"></a>2.函数参数的默认值</h3><p>函数的参数可以有默认值，在调用函数时，可以不给实参，函数将使用默认值；若只想修改部分参数的值，可以使用带名参数，指定参数名称为参数赋值。代码示例：</p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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><code class="hljs scala"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">databaseConnect</span></span>(ip: <span class="hljs-type">String</span> = <span class="hljs-string">&quot;127.0.0.1&quot;</span>, port: <span class="hljs-type">Int</span> = <span class="hljs-number">3306</span>,<br>                    user: <span class="hljs-type">String</span> = <span class="hljs-string">&quot;root&quot;</span>, password: <span class="hljs-type">String</span> = <span class="hljs-string">&quot;root&quot;</span>): <span class="hljs-type">Unit</span> = &#123;<br>  println(ip + port + user + password)<br>&#125;<br><span class="hljs-comment">// 调用，当需要不按顺序修改参数的值时，可以指定参数名称赋值</span><br>databaseConnect(<span class="hljs-string">&quot;192.168.1.2&quot;</span>, password = <span class="hljs-string">&quot;admin123&quot;</span>)<br></code></pre></td></tr></table></figure><p><em>注：Scala函数的形参默认是<code>val</code>类型，因此不能在函数中修改。</em></p><h3 id="3-可变参数"><a href="#3-可变参数" class="headerlink" title="3.可变参数"></a>3.可变参数</h3><p>Scala函数支持可变参数，使用星号表示，例：</p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs scala"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">func1</span></span>(args: <span class="hljs-type">Int</span>*): <span class="hljs-type">Unit</span> = &#123;<br>  <span class="hljs-keyword">for</span> (i &lt;- args) &#123;<br>    print(i)<br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>其中<code>args</code>可以接受多个参数，类型都为<code>Int</code>，<code>args</code>为一个序列，包含了多个参数的值。可以和普通参数一同使用，但使用时可变参数一定放在最后。调用方法：</p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs scala">func1(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">6</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">98</span>)  <br></code></pre></td></tr></table></figure><h3 id="4-过程"><a href="#4-过程" class="headerlink" title="4.过程"></a>4.过程</h3><p>返回值类型为<code>Unit</code>的函数称为过程。</p><h2 id="十二、函数的递归"><a href="#十二、函数的递归" class="headerlink" title="十二、函数的递归"></a>十二、函数的递归</h2><p>递归函数不能自动判断返回类型，必须指定返回的数据类型。</p><h2 id="十三、惰性函数"><a href="#十三、惰性函数" class="headerlink" title="十三、惰性函数"></a>十三、惰性函数</h2><p>一个有返回值的函数，当它被调用且返回值被赋值给一个被声明为<code>lazy</code>的<code>val</code>类型时，这个函数将的执行将被推迟，直到这个<code>val</code>类型被使用时，函数才会被执行。在此之前，即使存在赋值语句，函数仍不会被执行。代码示例：</p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs scala"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">t2</span></span>(n: <span class="hljs-type">Int</span>): <span class="hljs-type">Int</span> = &#123;<br>  println(<span class="hljs-string">&quot;函数被执行&quot;</span>)<br>  n * <span class="hljs-number">2</span><br>&#125;<br><span class="hljs-keyword">lazy</span> <span class="hljs-keyword">val</span> result: <span class="hljs-type">Int</span> = t2(<span class="hljs-number">200</span>)<br>println(<span class="hljs-string">&quot;赋值语句结束&quot;</span>)<br>println(result)<br></code></pre></td></tr></table></figure><p>这段代码的执行结果为：</p><pre>赋值语句结束函数被执行400</pre><p>即<code>result</code>被赋值时，<code>t2</code>函数并不会立即执行，而是在使用<code>result</code>的值时才会被执行。</p><p><em>注：<code>lazy</code>关键字只能用于<code>val</code>类型的值，不能修饰<code>var</code>类型的变量。当<code>lazy</code>修饰一个变量时，该变量值的分配也会被推迟。</em></p><h2 id="十四、异常的类型"><a href="#十四、异常的类型" class="headerlink" title="十四、异常的类型"></a>十四、异常的类型</h2><p>Scala异常体系沿用了Java的异常体系，Java的各种异常类型被Scala使用。</p><h2 id="十五、异常的处理"><a href="#十五、异常的处理" class="headerlink" title="十五、异常的处理"></a>十五、异常的处理</h2><p>Scala中也使用<code>try</code>和<code>catch</code>来捕获和处理异常，但是用法和Java不尽相同，捕获和处理异常的代码示例如下：</p> <figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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><code class="hljs scala"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">mayException</span></span>(): <span class="hljs-type">Int</span> = &#123;<br>  <span class="hljs-number">5</span> / <span class="hljs-number">0</span><br>&#125;<br><span class="hljs-keyword">try</span> &#123; <br>  mayException()<br>&#125; <span class="hljs-keyword">catch</span> &#123;<br>  <span class="hljs-keyword">case</span> e: <span class="hljs-type">ArithmeticException</span> =&gt; println(e.getMessage)<br>  <span class="hljs-keyword">case</span> e: <span class="hljs-type">Exception</span> =&gt; &#123;<br>    println(<span class="hljs-string">&quot;捕获了一个异常&quot;</span>)<br>    e.printStackTrace()<br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>上述代码使用<code>try</code>和<code>catch</code>捕获并处理了一个算数异常，在<code>catch</code>块中，Scala使用<code>case</code>关键字来捕获异常，使用<code>=&gt;</code>关键字来声明处理异常的代码，这里可以是单行代码也可以是代码块。可以添加<code>finally</code>块，此块中的代码无论异常捕获发生与否都会执行。</p><h3 id="1-声明某个方法可能抛出异常"><a href="#1-声明某个方法可能抛出异常" class="headerlink" title="1.声明某个方法可能抛出异常"></a>1.声明某个方法可能抛出异常</h3><p>使用throw注解标注方法来声明这个方法可能抛出异常：</p><figure class="highlight scala"><table><tr><td class="gutter"><pre><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><code class="hljs scala"><span class="hljs-meta">@throws</span>(classOf[<span class="hljs-type">ArithmeticException</span>])<br><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">mayException</span></span>(): <span class="hljs-type">Int</span> = &#123;<br>  <span class="hljs-number">5</span> / <span class="hljs-number">0</span><br>&#125;<br></code></pre></td></tr></table></figure><p>其中<code>classOf[ArithmeticException]</code>相当于Java中的<code>ArithmeticException.class</code>。</p><h2 id="十六、面向对象编程"><a href="#十六、面向对象编程" class="headerlink" title="十六、面向对象编程"></a>十六、面向对象编程</h2><h3 id="1-Scala类的属性和Java的关系"><a href="#1-Scala类的属性和Java的关系" class="headerlink" title="1.Scala类的属性和Java的关系"></a>1.Scala类的属性和Java的关系</h3><p>Scala中的类的属性默认都是<code>private</code>，而在底层Java字节码中，生成了对应的<code>public</code>的<code>getter</code>和<code>setter</code>方法。</p><p>Scala中的类默认权限修饰符是<code>public</code>，不能显式添加<code>public</code>修饰符，且一个类文件中可以包含多个类，且全部可以为<code>public</code>。 </p><h3 id="2-属性定义"><a href="#2-属性定义" class="headerlink" title="2.属性定义"></a>2.属性定义</h3><p>Scala在类中定义属性时，需要显式初始化，而不能像Java中声明后不进行初始化，可以根据初始化的值进行自动类型判断。</p><p>如果初始化的值为<code>null</code>，则一定要加类型，否则属性的类型将被自动推断为<code>Null</code>。</p><p>若需要为各个类型的变量赋值默认值，使用下划线“_”进行赋值，下划线为各个类型赋值的默认值见表：</p><p><img src="/img/scala-byte-short-int-long-float-double-string-null-false.jpg" alt="Byte Short Int Long  Float Double  string  0.0  null  false "></p><h3 id="3-对象"><a href="#3-对象" class="headerlink" title="3.对象"></a>3.对象</h3><p>Scala对象内存布局和Java相同，栈中的引用对象的变量保存指向堆中对象的内存地址。</p><p>使用<code>new</code>关键字进行对象的实例化，对于构造器没有形参的类，可以不加括号。</p><h3 id="4-类"><a href="#4-类" class="headerlink" title="4.类"></a>4.类</h3><p>Scala中的类定义使用<code>class</code>关键字，构造器声明在<code>class</code>关键字类名后面，其成员变量使用<code>var</code>或者<code>val</code>在类的块中进行声明，声明为<code>private</code>的属性使用<code>getter</code>和<code>setter</code>进行外部访问，声明方法和Java不同，示例代码：</p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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><code class="hljs scala"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TestClass</span>(<span class="hljs-params">left: <span class="hljs-type">String</span>, right: <span class="hljs-type">String</span></span>) </span>&#123;<br>  <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> _pp:  <span class="hljs-type">String</span> =  _<br>  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">pp</span> </span>= _pp<br>  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">pp_=</span></span>(newValue: <span class="hljs-type">String</span>): <span class="hljs-type">Unit</span> = &#123;<br>    _pp = newValue<br>  &#125;<br>  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">greet</span></span>(name:  <span class="hljs-type">String</span>):  <span class="hljs-type">Unit</span> = &#123;<br>    println(left + name + right)<br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>注：</p><ul><li>在<code>TestClass</code>后面的参数列表为默认构造器（主构造器）的形参列表，当参数名称前不加<code>var</code>或<code>val</code>时，其权限为<code>private</code>，添加<code>var</code>或<code>val</code>时，其权限为<code>public</code>。</li><li>权限为<code>private</code>的属性，getter方法名为属性名称；setter方法名为<code>属性名_=</code>。在调用时，可以直接使用等于号对属性值进行赋值，而不用显式调用函数名称。例：</li></ul><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs scala"><span class="hljs-keyword">val</span> t = <span class="hljs-keyword">new</span> <span class="hljs-type">TestClass</span>(left = <span class="hljs-string">&quot;left&quot;</span>, right = <span class="hljs-string">&quot;right&quot;</span>)<br>t.pp = <span class="hljs-string">&quot;?????&quot;</span><br></code></pre></td></tr></table></figure><ul><li>写在类名后面的构造器称为主构造器，在类定义中的方法名为<code>this</code>的构造器为辅助构造器。</li><li>主构造器在执行时会调用类定义中的所有语句，所以可以在定义类时进行属性的处理或者其他工作。</li><li>Scala通过构造器形参列表的不同来区分不同的构造器，辅助构造器在定义时必须显式调用主构造器（直接调用或调用其他调用了主构造器的构造器）。</li><li>为了和Java的可互操作性，属性可以使用<code>@BeanProperty</code>注解标注，用于生成和Java兼容的<code>getXxx</code>和<code>setXxx</code>方法，与原有的Scala的getter和setter方法可以共存。</li></ul><h3 id="5-类的继承"><a href="#5-类的继承" class="headerlink" title="5.类的继承"></a>5.类的继承</h3><p>Scala和Java相同，都是只能单继承，使用<code>extends</code>关键字集成基类。</p><h3 id="6-object"><a href="#6-object" class="headerlink" title="6.object"></a>6.object</h3><p>Scala中不仅有<code>class</code>关键字来声明一个类，还有<code>object</code>关键字来声明对象类型。对象类型指的是这个对象类型定义所代表的类的单例对象。</p><blockquote><p>Objects are single instances of their own definitions. You can think of them as singletons of their own classes.</p></blockquote><p>在编译后的Java字节码中，表现为一个伴生类，我们在程序中使用的是伴生类的实例化对象。</p><p>Scala程序的入口声明在一个<code>object</code>中，方法名为<code>main</code>，参数列表为字符串数组，例：</p><figure class="highlight scala"><table><tr><td class="gutter"><pre><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><code class="hljs scala"><span class="hljs-class"><span class="hljs-keyword">object</span> <span class="hljs-title">Exec</span> </span>&#123;<br>  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span></span>(args:  <span class="hljs-type">Array</span>[<span class="hljs-type">String</span>]): <span class="hljs-type">Unit</span> = &#123;<br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="7-trait"><a href="#7-trait" class="headerlink" title="7.trait"></a>7.trait</h3><p>trait中文名为特质，Scala中的<code>trait</code>相当于Java中的<code>interface</code>，用于在类之间共享接口和属性，但它不能被实例化。</p><h2 id="十七、包机制"><a href="#十七、包机制" class="headerlink" title="十七、包机制"></a>十七、包机制</h2><h3 id="1-包的声明"><a href="#1-包的声明" class="headerlink" title="1.包的声明"></a>1.包的声明</h3><p>Scala中的包比Java中更灵活，有三种声明包的方式：</p><p>第一种：</p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs scala"><span class="hljs-keyword">package</span> cn.maphical.modules<br><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PackageImport</span> </span>&#123;&#125;<br></code></pre></td></tr></table></figure><p>第二种：</p><figure class="highlight scala"><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><code class="hljs scala"><span class="hljs-keyword">package</span> cn.maphical<br><span class="hljs-keyword">package</span> modules  <br><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PackageImport</span></span>&#123;&#125;  <br></code></pre></td></tr></table></figure><p>第三种：</p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs scala"><span class="hljs-keyword">package</span> cn.maphical &#123;<br>  <span class="hljs-keyword">package</span> modules &#123;<br>    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PackageImport</span> </span>&#123;&#125;<br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>因此可以在同一个文件中声明不同的包，更加灵活。</p><p>子包可以使用父包中的内容，而不用显式引入。 </p><h4 id="就近原则"><a href="#就近原则" class="headerlink" title="就近原则"></a>就近原则</h4><p>如果有两个相同名称的类，不使用包路径直接引用，则会优先使用本包中的类，由近及远。</p><h3 id="2-包对象"><a href="#2-包对象" class="headerlink" title="2.包对象"></a>2.包对象</h3><p>一个包对象（package object）是对包功能的补充。现有一个包<code>cn.maphical.modules</code>，定义这个包的包对象，在其父包<code>cn.maphical</code>中定义<code>package object modules</code>，具体代码如下：</p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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><code class="hljs scala"><span class="hljs-keyword">package</span> cn.maphical<br><span class="hljs-keyword">package</span> <span class="hljs-class"><span class="hljs-keyword">object</span> <span class="hljs-title">modules</span> </span>&#123;<br>  <span class="hljs-keyword">val</span> name = <span class="hljs-string">&quot;Scala&quot;</span><br>  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">sayHi</span></span>(): <span class="hljs-type">Unit</span> = &#123;<br>    println(<span class="hljs-string">&quot;HI  from package object&quot;</span>)<br>  &#125;<br>&#125;<br><span class="hljs-keyword">package</span> modules &#123;<br>  <span class="hljs-class"><span class="hljs-keyword">object</span> <span class="hljs-title">Test</span> </span>&#123;<br>    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span></span>(args: <span class="hljs-type">Array</span>[<span class="hljs-type">String</span>]): <span class="hljs-type">Unit</span> = &#123;<br>      println(<span class="hljs-string">&quot;name=&quot;</span>  + name)<br>      sayHi()<br>    &#125;<br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>在cn.maphical包中定义了<code>modules</code>的包对象后，其内部的变量和方法都可以在<code>modules</code>包中使用了。</p><h4 id="包对象的底层机制"><a href="#包对象的底层机制" class="headerlink" title="包对象的底层机制"></a>包对象的底层机制</h4><p>使用反编译工具对上述代码进行反编译，可以得出：包对象在字节码中的存在形式为名为<code>package</code>和<code>package$</code>的两个类，<code>package$</code>中存在一个静态对象，此对象的方法被<code>Test</code>类中的<code>main</code>方法调用，反编译后的代码如下：</p><p><em>文件名：package$.class</em></p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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><code class="hljs scala"><span class="hljs-keyword">package</span> cn.maphical.modules;<br>public <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">package$</span> </span>&#123;<br>  public static <span class="hljs-keyword">final</span> <span class="hljs-keyword">package</span>$ <span class="hljs-type">MODULE</span>$ = <span class="hljs-keyword">new</span> <span class="hljs-keyword">package</span>$();<br>  <span class="hljs-keyword">private</span> static <span class="hljs-keyword">final</span> <span class="hljs-type">String</span> name = <span class="hljs-string">&quot;Scala&quot;</span>;<br>  public  <span class="hljs-type">String</span> name() &#123;<br>    <span class="hljs-keyword">return</span> name;<br>  &#125;<br>  public void sayHi() &#123;<br>    scala.<span class="hljs-type">Predef</span>$.<span class="hljs-type">MODULE</span>$.println(<span class="hljs-string">&quot;HI  from package object&quot;</span>);<br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p><em>文件名：package.class</em></p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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><code class="hljs scala"><span class="hljs-keyword">package</span> cn.maphical.modules;<br><span class="hljs-keyword">import</span> scala.reflect.<span class="hljs-type">ScalaSignature</span>;<br>public <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span>  <span class="hljs-title">package</span> </span>&#123;<br>  public static void sayHi() &#123;<br>    <span class="hljs-keyword">package</span>$.<span class="hljs-type">MODULE</span>$.sayHi();<br>  &#125;<br>  public static <span class="hljs-type">String</span> name() &#123;<br>    <span class="hljs-keyword">return</span> <span class="hljs-keyword">package</span>$.<span class="hljs-type">MODULE</span>$.name();<br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p><em>文件名：Test$.class</em></p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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><code class="hljs scala"><span class="hljs-keyword">package</span> cn.maphical.modules;<br>public <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Test$</span> </span>&#123;<br>  public static <span class="hljs-keyword">final</span> <span class="hljs-type">Test</span>$ <span class="hljs-type">MODULE</span>$ = <span class="hljs-keyword">new</span> <span class="hljs-type">Test</span>$();<br>  public void main(<span class="hljs-type">String</span>[] args) &#123;<br>    scala.<span class="hljs-type">Predef</span>$.<span class="hljs-type">MODULE</span>$.println((newStringBuilder(<span class="hljs-number">5</span>))<br>                                  .append(<span class="hljs-string">&quot;name=&quot;</span>)<br>                                  .append(<span class="hljs-keyword">package</span>$.<span class="hljs-type">MODULE</span>$.name())<br>                                  .toString());<br>    <span class="hljs-keyword">package</span>$.<span class="hljs-type">MODULE</span>$.sayHi();<br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p><code>Test$</code>中的<code>main</code>方法调用了<code>package$</code>中静态对象的各种方法，这些方法都是在Scala包对象中定义的。</p><p>包对象中可以定义任何内容，而不仅仅是变量和方法。 例如，包对象经常用于保存包级作用域的类型别名和隐式转换。 包对象甚至可以继承 Scala 的类和特质。</p><p>按照惯例，包对象的代码通常放在名为 <code>package.scala</code>的源文件中。</p><h3 id="3-伴生类和伴生对象"><a href="#3-伴生类和伴生对象" class="headerlink" title="3.伴生类和伴生对象"></a>3.伴生类和伴生对象</h3><p>在一个文件中出现了同名的类（class）和对象（object）时，class称为伴生类，object称为伴生对象。由于Scala中没有<code>static</code>关键字，为了和Java兼容（Java有<code>static</code>关键字），需要将静态内容写在伴生对象中，非静态内容写在伴生类中。</p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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><code class="hljs scala"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ClassAndObject</span> </span>&#123;<br>  <span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> text = <span class="hljs-string">&quot;Scala&quot;</span><br>&#125;<br><span class="hljs-class"><span class="hljs-keyword">object</span> <span class="hljs-title">ClassAndObject</span> </span>&#123;<br>  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test</span></span>(c: <span class="hljs-type">ClassAndObject</span>): <span class="hljs-type">Unit</span> = &#123;<br>    println(c.text)<br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>伴生对象可以访问伴生类中的私有方法和属性。</p><h3 id="4-包访问权限"><a href="#4-包访问权限" class="headerlink" title="4.包访问权限"></a>4.包访问权限</h3><p>Scala中的包访问权限与Java稍有不同，Scala中没有<code>public</code>关键字，其默认的方法和属性权限为<code>public</code>。</p><ul><li>private：私有权限，只在类的内部和伴生对象中可以使用。</li><li>protected：受保护权限，只能子类访问，与Java不同，同包不能访问。</li></ul><p>访问权限的扩大：</p><p><code>protected</code>和<code>private</code>关键字可以手动扩大权限，指定能够访问此内容的包：</p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs scala"><span class="hljs-keyword">private</span>[modules] <span class="hljs-keyword">val</span> text = <span class="hljs-string">&quot;Scala&quot;</span><br></code></pre></td></tr></table></figure><p>上述代码表示，<code>text</code>属性在<code>private</code>基础上，对<code>modules</code>包以及它的子包有可访问权限。</p><h3 id="5-包的引入"><a href="#5-包的引入" class="headerlink" title="5.包的引入"></a>5.包的引入</h3><p>与Java不同的点为：</p><ul><li><code>import</code>语句可以不出现在文件头部，可以在需要时再引入，但其作用域为其被包含的大括号。</li><li>引入所有包，使用下划线（_）</li><li>使用选择器导入部分包部分类，例：<code>import scala.collection.mutable.{HashMap, HashSet}</code>，即导入了<code>HashMap</code>和<code>HashSet</code>这两个类</li><li>将导入的类重命名，例：<code>import java.util.{HashMap =&gt; Map}</code>，将<code>HashMap</code>重命名为<code>Map</code></li></ul><h2 id="十八、柯里化"><a href="#十八、柯里化" class="headerlink" title="十八、柯里化"></a>十八、柯里化</h2><blockquote><p><em>在计算机科学中，柯里化（Currying）是把接受多个参数的函数变换成接受一个单一参数（最初函数的第一个参数）的函数，并且返回接受余下的参数且返回结果的新函数的技术。这个技术由</em> <em>Christopher Strachey</em> <em>以逻辑学家</em> <em>Haskell Curry</em> <em>命名的，尽管它是</em> <em>Moses Schnfinkel</em> <em>和</em> <em>Gottlob Frege</em> 发明的。</p><p><em>——百度百科</em></p></blockquote><p>Scala中的函数柯里化使用多个参数列表实现，一个函数可以有多个参数列表，而这些参数列表不需要必须同时提供。在只提供一个参数列表的实参时，函数返回带有这些参数的值的新函数，此新函数可以接受剩余的参数列表，此函数可以作为一个普通函数使用，并返回正确的值。</p><p>代码：</p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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><code class="hljs scala"><span class="hljs-class"><span class="hljs-keyword">object</span> <span class="hljs-title">CurryingTest</span> </span>&#123;<br>  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">curringTest</span></span>(seq: <span class="hljs-type">List</span>[<span class="hljs-type">Int</span>])(func: (<span class="hljs-type">Int</span>, <span class="hljs-type">Int</span>) =&gt; <span class="hljs-type">Int</span>): <span class="hljs-type">Int</span> = &#123;<br>    <span class="hljs-keyword">var</span> result = seq.head<br>    <span class="hljs-keyword">for</span> (item &lt;- seq; <span class="hljs-keyword">if</span> item != seq.head) &#123;<br>      result = func(result, item)<br>    &#125;<br>    result<br>  &#125;<br>  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span></span>(args: <span class="hljs-type">Array</span>[<span class="hljs-type">String</span>]): <span class="hljs-type">Unit</span> = &#123;<br>    <span class="hljs-keyword">val</span> cat = <span class="hljs-keyword">new</span> <span class="hljs-type">Cat</span><br>    <span class="hljs-keyword">val</span> list: <span class="hljs-type">List</span>[<span class="hljs-type">Int</span>] = <span class="hljs-type">List</span>[<span class="hljs-type">Int</span>](<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">6</span>, <span class="hljs-number">7</span>)<br>    <span class="hljs-keyword">val</span> result = curringTest(list) _    <span class="hljs-comment">// 这里使用下划线代替没有提供实参的参数列表</span><br>    println(result(_ * _))<br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>上述代码中的<code>result</code>既是<code>curryingTest</code>函数在接受了一个<code>List</code>后形成的函数，此函数可以作为一个新的函数进行调用，并接受原有函数的未提供参数列表作为其参数列表。</p><h2 id="十九、内部类"><a href="#十九、内部类" class="headerlink" title="十九、内部类"></a>十九、内部类</h2><p>Scala的内部类和Java不同之处在于，Java中在某类的内部定义的类，是此类的成员变量，和外部类绑定。而Scala中在某类内部定义的类，和此类的对象绑定，即每个此类实例化产生的每个对象都有一个此内部类的定义，不同对象的此内部类是“不同的类型”。</p><p>例：</p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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><code class="hljs scala"><span class="hljs-class"><span class="hljs-keyword">object</span> <span class="hljs-title">InnerClass</span> </span>&#123;<br>  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span></span>(args: <span class="hljs-type">Array</span>[<span class="hljs-type">String</span>]): <span class="hljs-type">Unit</span> = &#123;<br>    <span class="hljs-keyword">val</span> a = <span class="hljs-keyword">new</span> <span class="hljs-type">InnerClassTest</span><br>    <span class="hljs-keyword">val</span> b = <span class="hljs-keyword">new</span> <span class="hljs-type">InnerClassTest</span><br>    <span class="hljs-keyword">val</span> innerA = a.newInnerA<br>    <span class="hljs-keyword">val</span> innerB = b.newInnerA<br>    a.setInnerInst(innerA)  <span class="hljs-comment">// 正常赋值</span><br>    a.setInnerInst(innerB)  <span class="hljs-comment">// 非法赋值</span><br>  &#125;<br>&#125;<br><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">InnerClassTest</span> </span>&#123;<br>  <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">InnerClassA</span> </span>&#123;&#125;<br>  <span class="hljs-keyword">var</span> innerClassInst: <span class="hljs-type">InnerClassA</span> = _<br>  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">newInnerA</span> </span>= <span class="hljs-keyword">new</span> <span class="hljs-type">InnerClassA</span><br>  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">setInnerInst</span></span>(inst: <span class="hljs-type">InnerClassA</span>): <span class="hljs-type">Unit</span> = <span class="hljs-keyword">this</span>.innerClassInst = inst<br>&#125;<br></code></pre></td></tr></table></figure><p>上述中将<code>innerB</code>对象赋值给对象<code>a</code>的成员变量时出错，原因是<code>innerA</code>和<code>innerB</code>不是同一个类型，它们的类型分别是：<code>a.InnerClassA</code>和<code>b.InnerClassA</code>（和对象绑定）。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;由于工作的需要，最近开始学习Scala和Flink，下面是学习途中做的笔记，略作了修改，希望能够给正在学习Scala的朋友一些帮助。&lt;/p&gt;
&lt;h2 id=&quot;一、变量的定义&quot;&gt;&lt;a href=&quot;#一、变量的定义&quot; class=&quot;headerlink&quot; title=&quot;一、变量</summary>
      
    
    
    
    <category term="Computer Science" scheme="https://maphical.cn/categories/Computer-Science/"/>
    
    <category term="Programming Language" scheme="https://maphical.cn/categories/Computer-Science/Programming-Language/"/>
    
    <category term="Scala" scheme="https://maphical.cn/categories/Computer-Science/Programming-Language/Scala/"/>
    
    
  </entry>
  
  <entry>
    <title>在SpringBoot中使用Activiti7（不使用activiti-spring-boot-starter）</title>
    <link href="https://maphical.cn/2020/08/use-activiti-7-in-springboot/"/>
    <id>https://maphical.cn/2020/08/use-activiti-7-in-springboot/</id>
    <published>2020-08-30T14:40:36.000Z</published>
    <updated>2025-10-11T16:10:29.448Z</updated>
    
    <content type="html"><![CDATA[<p>将Activiti7整合进入SpringBoot中，而不使用官方提供的activiti-spring-boot-starter。</p><h2 id="一、POM引入Activiti依赖"><a href="#一、POM引入Activiti依赖" class="headerlink" title="一、POM引入Activiti依赖"></a>一、POM引入Activiti依赖</h2><p>**文件名：**pom.xml</p><p>**类型：**Maven项目配置</p><p>**作用：**引入Activiti7库</p> <figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">properties</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">activiti.version</span>&gt;</span>7.1.0.M6<span class="hljs-tag">&lt;/<span class="hljs-name">activiti.version</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">properties</span>&gt;</span><br><br><span class="hljs-tag">&lt;<span class="hljs-name">dependencies</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.activiti<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>activiti-engine<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>$&#123;activiti.version&#125;<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br>    <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br><br>    <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.activiti<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>activiti-spring<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>$&#123;activiti.version&#125;<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br>    <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br><br>    <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.activiti<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>activiti-bpmn-model<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>$&#123;activiti.version&#125;<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br>    <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br><br>    <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.activiti<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>activiti-bpmn-converter<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>$&#123;activiti.version&#125;<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br>    <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br><br>    <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.activiti<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>activiti-json-converter<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>$&#123;activiti.version&#125;<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br>    <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br><br>    <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.activiti<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>activiti-bpmn-layout<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>$&#123;activiti.version&#125;<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br>    <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">dependencies</span>&gt;</span><br></code></pre></td></tr></table></figure><h2 id="二、注册Activiti相关的Bean"><a href="#二、注册Activiti相关的Bean" class="headerlink" title="二、注册Activiti相关的Bean"></a>二、注册Activiti相关的Bean</h2><p>**文件名：**ApplicationConfiguration.java</p><p>**类型：**Spring配置注解</p><p>**作用：**注册ServiceBean需要使用的SpringProcessEngineConfiguration和ProcessEngineFactoryBean</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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><code class="hljs java"><span class="hljs-keyword">import</span> org.activiti.spring.ProcessEngineFactoryBean;<br><span class="hljs-keyword">import</span> org.activiti.spring.SpringProcessEngineConfiguration;<br><span class="hljs-keyword">import</span> org.slf4j.Logger;<br><span class="hljs-keyword">import</span> org.slf4j.LoggerFactory;<br><span class="hljs-keyword">import</span> org.springframework.context.annotation.Bean;<br><span class="hljs-keyword">import</span> org.springframework.context.annotation.Configuration;<br><span class="hljs-keyword">import</span> org.springframework.transaction.PlatformTransactionManager;<br><span class="hljs-keyword">import</span> org.springframework.web.servlet.config.annotation.WebMvcConfigurer;<br><br><span class="hljs-keyword">import</span> javax.sql.DataSource;<br><br><span class="hljs-meta">@Configuration</span><br><span class="hljs-meta">@Order(300)</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">ApplicationConfiguration</span> &#123;<br>    <span class="hljs-keyword">private</span> <span class="hljs-type">Logger</span> <span class="hljs-variable">logger</span> <span class="hljs-operator">=</span> LoggerFactory.getLogger(ApplicationConfiguration.class);<br>    <span class="hljs-keyword">private</span> DataSource dataSource;<br>    <span class="hljs-keyword">private</span> PlatformTransactionManager transactionManager;<br><br>    <span class="hljs-keyword">public</span> <span class="hljs-title function_">ApplicationConfiguration</span><span class="hljs-params">(DataSource dataSource, PlatformTransactionManager transactionManager)</span> &#123;<br>        <span class="hljs-built_in">this</span>.dataSource = dataSource;<br>        <span class="hljs-built_in">this</span>.transactionManager = transactionManager;<br>    &#125;<br><br>    <span class="hljs-meta">@Bean</span><br>    <span class="hljs-keyword">public</span> SpringProcessEngineConfiguration <span class="hljs-title function_">processEngineConfiguration</span><span class="hljs-params">()</span> &#123;<br>        logger.info(<span class="hljs-string">&quot;注册Activiti流程引擎配置……&quot;</span>);<br>        <span class="hljs-type">SpringProcessEngineConfiguration</span> <span class="hljs-variable">springProcessEngineConfiguration</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">SpringProcessEngineConfiguration</span>();<br>        springProcessEngineConfiguration.setDataSource(dataSource);<br>        springProcessEngineConfiguration.setTransactionManager(transactionManager);<br>        springProcessEngineConfiguration.setDatabaseSchemaUpdate(<span class="hljs-string">&quot;true&quot;</span>);<br>        <span class="hljs-keyword">return</span> springProcessEngineConfiguration;<br>    &#125;<br><br>    <span class="hljs-meta">@Bean</span><br>    <span class="hljs-keyword">public</span> ProcessEngineFactoryBean <span class="hljs-title function_">processEngine</span><span class="hljs-params">()</span> &#123;<br>        logger.info(<span class="hljs-string">&quot;注册Activiti流程引擎……&quot;</span>);<br>        <span class="hljs-type">ProcessEngineFactoryBean</span> <span class="hljs-variable">processEngineFactoryBean</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ProcessEngineFactoryBean</span>();<br>        processEngineFactoryBean.setProcessEngineConfiguration(processEngineConfiguration());<br>        <span class="hljs-keyword">return</span> processEngineFactoryBean;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>**文件名：**ActivitiConfiguration.java</p><p>**类型：**Spring配置注解</p><p>**作用：**注册各个ServiceBean，其中的Order注解确保在ProcessEngineBean被创建之后再执行此配置中各种服务Bean的创建。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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><code class="hljs java"><span class="hljs-keyword">import</span> org.activiti.engine.*;<br><span class="hljs-keyword">import</span> org.slf4j.Logger;<br><span class="hljs-keyword">import</span> org.slf4j.LoggerFactory;<br><span class="hljs-keyword">import</span> org.springframework.context.annotation.Bean;<br><span class="hljs-keyword">import</span> org.springframework.context.annotation.Configuration;<br><span class="hljs-keyword">import</span> org.springframework.core.annotation.Order;<br><br><span class="hljs-meta">@Configuration</span><br><span class="hljs-meta">@Order(301)</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">ActivitiConfiguration</span> &#123;<br>    <span class="hljs-keyword">private</span> <span class="hljs-type">Logger</span> <span class="hljs-variable">logger</span> <span class="hljs-operator">=</span> LoggerFactory.getLogger(ActivitiConfiguration.class);<br>    <span class="hljs-keyword">private</span> ProcessEngine processEngine;<br><br>    <span class="hljs-keyword">public</span> <span class="hljs-title function_">ActivitiConfiguration</span><span class="hljs-params">(ProcessEngine processEngine)</span> &#123;<br>        <span class="hljs-built_in">this</span>.processEngine = processEngine;<br>    &#125;<br><br>    <span class="hljs-meta">@Bean</span><br>    <span class="hljs-keyword">public</span> RepositoryService <span class="hljs-title function_">repositoryService</span><span class="hljs-params">()</span> &#123;<br>        logger.info(<span class="hljs-string">&quot;注册Activiti资源服务……&quot;</span>);<br>        <span class="hljs-keyword">return</span> processEngine.getRepositoryService();<br>    &#125;<br><br>    <span class="hljs-meta">@Bean</span><br>    <span class="hljs-keyword">public</span> RuntimeService <span class="hljs-title function_">runtimeService</span><span class="hljs-params">()</span> &#123;<br>        logger.info(<span class="hljs-string">&quot;注册Activiti运行时服务……&quot;</span>);<br>        <span class="hljs-keyword">return</span> processEngine.getRuntimeService();<br>    &#125;<br><br>    <span class="hljs-meta">@Bean</span><br>    <span class="hljs-keyword">public</span> TaskService <span class="hljs-title function_">taskService</span><span class="hljs-params">()</span> &#123;<br>        logger.info(<span class="hljs-string">&quot;注册Activiti任务服务……&quot;</span>);<br>        <span class="hljs-keyword">return</span> processEngine.getTaskService();<br>    &#125;<br><br>    <span class="hljs-meta">@Bean</span><br>    <span class="hljs-keyword">public</span> HistoryService <span class="hljs-title function_">historyService</span><span class="hljs-params">()</span> &#123;<br>        logger.info(<span class="hljs-string">&quot;注册Activiti历史服务……&quot;</span>);<br>        <span class="hljs-keyword">return</span> processEngine.getHistoryService();<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>以上。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;将Activiti7整合进入SpringBoot中，而不使用官方提供的activiti-spring-boot-starter。&lt;/p&gt;
&lt;h2 id=&quot;一、POM引入Activiti依赖&quot;&gt;&lt;a href=&quot;#一、POM引入Activiti依赖&quot; class=&quot;heade</summary>
      
    
    
    
    <category term="Computer Science" scheme="https://maphical.cn/categories/Computer-Science/"/>
    
    <category term="Frameworks &amp; Tools" scheme="https://maphical.cn/categories/Computer-Science/Frameworks-Tools/"/>
    
    <category term="Activiti" scheme="https://maphical.cn/categories/Computer-Science/Frameworks-Tools/Activiti/"/>
    
    
  </entry>
  
  <entry>
    <title>使用Java连接Kafka发布和订阅消息</title>
    <link href="https://maphical.cn/2020/08/connect-kafka-in-java/"/>
    <id>https://maphical.cn/2020/08/connect-kafka-in-java/</id>
    <published>2020-08-13T12:53:00.000Z</published>
    <updated>2025-10-11T16:10:29.448Z</updated>
    
    <content type="html"><![CDATA[<h2 id="一、Kafka库"><a href="#一、Kafka库" class="headerlink" title="一、Kafka库"></a>一、Kafka库</h2><p>通过Maven导入Kafka Client库：</p><p>在项目的POM中导入：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.apache.kafka<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>kafka-clients<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>2.5.0<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.slf4j<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>slf4j-simple<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>1.7.9<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br></code></pre></td></tr></table></figure><p>导入slf4j-simple用于解决org.slf4j.impl.StaticLoggerBinder无法加载的错误，也可以引入其他日志库。</p><h2 id="二、发布消息"><a href="#二、发布消息" class="headerlink" title="二、发布消息"></a>二、发布消息</h2><p>实例化一个<code>java.util.Properties</code>类，写入服务器地址和序列化反序列化的类配置：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-type">Properties</span> <span class="hljs-variable">p</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Properties</span>();  <br>p.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, <span class="hljs-string">&quot;192.168.1.228:9092&quot;</span>);<br>p.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);<br>p.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);  <br></code></pre></td></tr></table></figure><p>实例化一个<code>KafkaProducer</code>，<code>Properties</code>作为参数传入：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">KafkaProducer&lt;String,String&gt; kafkaProducer = <span class="hljs-keyword">new</span> <span class="hljs-title class_">KafkaProducer</span>&lt;&gt;(p);<br></code></pre></td></tr></table></figure><p>实例化一个<code>ProducerRecord</code>，第一个参数为Topic名称，第二个参数为消息体：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">ProducerRecord&lt;String,String&gt; record = <span class="hljs-keyword">new</span> <span class="hljs-title class_">ProducerRecord</span>&lt;&gt;(topic, msg);<br></code></pre></td></tr></table></figure><p>使用<code>KafkaProducer</code>发送这个Record：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">kafkaProducer.send(record);<br></code></pre></td></tr></table></figure><p>全部发送完成后调用close方法关闭这个Producer。</p><h2 id="三、订阅消息"><a href="#三、订阅消息" class="headerlink" title="三、订阅消息"></a>三、订阅消息</h2><p>实例化<code>Properties</code>类，在上面代码片段基础上加一个消费者的分组名称：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-type">Properties</span> <span class="hljs-variable">p</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Properties</span>();<br>p.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, <span class="hljs-string">&quot;192.168.1.228:9092&quot;</span>);<br>p.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);<br>p.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);<br>p.put(ConsumerConfig.GROUP_ID_CONFIG, <span class="hljs-string">&quot;MKMTest&quot;</span>);    <span class="hljs-comment">//消费者分组名称</span><br></code></pre></td></tr></table></figure><p>实例化一个<code>KafkaConsumer</code>，<code>Properties</code>作为参数传入，并订阅Topic：</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><code class="hljs java">KafkaConsumer&lt;String,String&gt; kafkaConsumer = <span class="hljs-keyword">new</span> <span class="hljs-title class_">KafkaConsumer</span>&lt;&gt;(p);<br>kafkaConsumer.subscribe(Collections.singletonList(topic));    <span class="hljs-comment">//订阅消息  </span><br></code></pre></td></tr></table></figure><p>在循环中获取消息，使用<code>kafkaConsumer.poll</code>方法获取消息数组，并通过循环读取每个消息：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">while</span> (<span class="hljs-literal">true</span>) &#123;<br>ConsumerRecords&lt;String,String&gt; records = kafkaConsumer.poll(Duration.ofMillis(<span class="hljs-number">500</span>));<br><span class="hljs-keyword">for</span> (ConsumerRecord&lt;String,String&gt; record : records)&#123;<br>System.out.println(String.format(<span class="hljs-string">&quot;topic:%s, offset:%d, 消息:%s&quot;</span>,<br>                                     record.topic(), record.offset(), record.value()));<br>&#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>其中，record的三个方法分别返回：</p><ul><li>topic：消息来源主题</li><li>offset：消息在分区中的偏移</li><li>value：消息的值</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;一、Kafka库&quot;&gt;&lt;a href=&quot;#一、Kafka库&quot; class=&quot;headerlink&quot; title=&quot;一、Kafka库&quot;&gt;&lt;/a&gt;一、Kafka库&lt;/h2&gt;&lt;p&gt;通过Maven导入Kafka Client库：&lt;/p&gt;
&lt;p&gt;在项目的POM中导入：&lt;/p&gt;</summary>
      
    
    
    
    <category term="Computer Science" scheme="https://maphical.cn/categories/Computer-Science/"/>
    
    <category term="Frameworks &amp; Tools" scheme="https://maphical.cn/categories/Computer-Science/Frameworks-Tools/"/>
    
    <category term="Kafka" scheme="https://maphical.cn/categories/Computer-Science/Frameworks-Tools/Kafka/"/>
    
    
  </entry>
  
  <entry>
    <title>Activiti-app自带平台使用概述</title>
    <link href="https://maphical.cn/2020/08/activiti-app-usage/"/>
    <id>https://maphical.cn/2020/08/activiti-app-usage/</id>
    <published>2020-08-01T07:48:00.000Z</published>
    <updated>2025-10-11T16:10:29.448Z</updated>
    
    <content type="html"><![CDATA[<p>最近工作中需要使用到Activiti工作流引擎，下载源码之后在其中发现了Activiti-app.war文件，部署之后发现是一个使用Activiti的官方网站Sample，在网上搜索了使用方法发现很少，现在把自己研究后总结的大致使用方法贴出来。</p><h2 id="一、管理用户"><a href="#一、管理用户" class="headerlink" title="一、管理用户"></a>一、管理用户</h2><p>在Identity Management应用中，用户管理分为3个部分（Administrator）：</p><ul><li>用户的创建和修改</li><li>用户组的创建的管理</li><li>自己的资料管理</li></ul><p><img src="data:image/png;base64,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" alt="img"></p><p>在创建流程之前首先进行人员用户的创建，然后在流程制作中即可将任务委托给各个用户。用户由管理员创建，管理员初始密码为：admin，test。普通用户在用户管理页面中只能管理自己的资料。</p><p><img src="data:image/png;base64,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" alt="Users  Groups  Create user  Personal  Matchin  Found 5 matching user(s), showing 1 to 5  admin  ligang  liuxiaoming  wangqiang  zhanghong  Email  admin  I igang@test.com  liuxiaoming@test.com  wangqiang@test.com  zhanghong@test.com  Name  Administrator "></p><p>创建用户组之后可以将用户添加进入组中，在流程中可以使用组来进行任务的分配。</p><h2 id="二、管理流程"><a href="#二、管理流程" class="headerlink" title="二、管理流程"></a>二、管理流程</h2><p>在主页面的Kickstart App是用来制作流程和发布流程的应用。包括4各部分：Processes、Forms、Decision Tables和Apps。</p><p><img src="data:image/png;base64,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" alt="img"></p><h3 id="1-Processes部分"><a href="#1-Processes部分" class="headerlink" title="1.Processes部分"></a>1.Processes部分</h3><p>这个部分用来创建流程，使用可视化编辑器来编辑BPM流程图，且包括查看和导出功能。</p><h3 id="2-Forms部分"><a href="#2-Forms部分" class="headerlink" title="2.Forms部分"></a>2.Forms部分</h3><p>这个部分用来创建表单，表单是在流程中用来收集信息的，流程中的事件如果需要用户填写信息，可以在流程事件中的Referenced Form属性绑定一个创建好的表单，那么当流程被执行且执行人在查看此任务时，表格会显示出来供其填写。</p><h4 id="关于表单和流程变量的结合使用"><a href="#关于表单和流程变量的结合使用" class="headerlink" title="关于表单和流程变量的结合使用"></a>关于表单和流程变量的结合使用</h4><p>表格中填写的项可以在流程中被用作流程变量，在流程图制作中使用UEL表达式访问。在使用UEL表达式时，所使用的流程变量的名称就是表单中每一项的id。</p><p><img src="data:image/png;base64,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" alt="Edit fi  General  Label:  Override id?  Id:  result 1  Required  Options  Close "></p><h4 id="表单中的各种组件"><a href="#表单中的各种组件" class="headerlink" title="表单中的各种组件"></a>表单中的各种组件</h4><p>在创建表单时，有几种组件可以使用：</p><p><img src="data:image/png;base64,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" alt="img"></p><p>下面将这几种组件的用途分别描述一下：</p><table><thead><tr><th><strong>类型</strong></th><th><strong>作用</strong></th><th><strong>备注</strong></th></tr></thead><tbody><tr><td>Text</td><td>文本输入框</td><td></td></tr><tr><td>Multiline text</td><td>多行文本输入框</td><td></td></tr><tr><td>Number</td><td>数字输入框</td><td></td></tr><tr><td>Checkbox</td><td>单个单选框</td><td>在变量中使用${id&#x3D;&#x3D;true&#x2F;false}来访问</td></tr><tr><td>Date</td><td>日期输入框</td><td>点击后在框中选择</td></tr><tr><td>Dropdown</td><td>下拉列表</td><td>在Options栏中编辑选项，后续元素中使用${id&#x3D;&#x3D;”选项值”}来访问</td></tr><tr><td>Radio buttons</td><td>单选框组</td><td>一组单选框，内含多个选项，后续元素中UEL使用${id&#x3D;&#x3D;”选项值”}来访问</td></tr><tr><td>People</td><td>人员选择器</td><td></td></tr><tr><td>Group of people</td><td>人员组选择器</td><td></td></tr><tr><td>Upload</td><td>文件上传器</td><td>目前存在Bug，文件上传后无法查看，或上传不成功</td></tr><tr><td>Expression</td><td>UEL表达式，显示文本或表达式的值</td><td>一般用于信息的呈现，展示前置表单中完成的变量值</td></tr></tbody></table><h3 id="3-Decision-Tables部分"><a href="#3-Decision-Tables部分" class="headerlink" title="3.Decision Tables部分"></a>3.Decision Tables部分</h3><p>创建后在流程中使用，用来帮助流程根据一系列的变量的值进行决策的表格，用户通过设置此表格中的左边输入变量的状态来设置相应的右边一栏的输出变量的值，这些值可以在流程中的后续环节中被使用。</p><h3 id="4-Apps部分"><a href="#4-Apps部分" class="headerlink" title="4.Apps部分"></a>4.Apps部分</h3><p>用来发布制作好的流程模型，创建一个App并选择与之绑定的流程模型后，填写一些基本信息即可发布，发布之后所有用户都可查看并在App中启动流程和完成任务。</p><p>流程模型更新之后，原有的App如果不重新发布就不会应用新的更改。</p><h2 id="三、使用流程进行工作"><a href="#三、使用流程进行工作" class="headerlink" title="三、使用流程进行工作"></a>三、使用流程进行工作</h2><p>发布了App后就可以使用流程了，包括发起新流程实例、查看当前用户的待办任务、处理待办任务等，这些操作都可以通过主页面的Task应用来实现。在Task中可以查看到所有App的待办任务和流程实例，也可以选择不同的App图标进入不同的App中查看特定App的任务和流程实例等。</p><p><img src="data:image/png;base64,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" alt="img"></p><p>Task应用中包括两个部分。</p><h3 id="1-Tasks部分"><a href="#1-Tasks部分" class="headerlink" title="1.Tasks部分"></a>1.Tasks部分</h3><p>在Tasks部分中，可以查看所有的待办任务，并且可以在其中完成任务（填表单等）。</p><p>可以脱离流程实例创建新任务，并将任务委托给不同的用户，包括自己。</p><h3 id="2-Processes部分"><a href="#2-Processes部分" class="headerlink" title="2.Processes部分"></a>2.Processes部分</h3><p>Processes部分可以查看当前用户参与的所有流程实例，在流程实例中查看历史活动信息和每个参与者的操作详情、表单填写信息等。</p><p>在这里，用户可以发起新的流程实例，也可以将自己发起的流程实例进行删除操作。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;最近工作中需要使用到Activiti工作流引擎，下载源码之后在其中发现了Activiti-app.war文件，部署之后发现是一个使用Activiti的官方网站Sample，在网上搜索了使用方法发现很少，现在把自己研究后总结的大致使用方法贴出来。&lt;/p&gt;
&lt;h2 id=&quot;一、</summary>
      
    
    
    
    <category term="Computer Science" scheme="https://maphical.cn/categories/Computer-Science/"/>
    
    <category term="Frameworks &amp; Tools" scheme="https://maphical.cn/categories/Computer-Science/Frameworks-Tools/"/>
    
    <category term="Activiti" scheme="https://maphical.cn/categories/Computer-Science/Frameworks-Tools/Activiti/"/>
    
    
  </entry>
  
  <entry>
    <title>使用Hexo搭建可自动同步的个人博客</title>
    <link href="https://maphical.cn/2020/03/build-blog-using-hexo/"/>
    <id>https://maphical.cn/2020/03/build-blog-using-hexo/</id>
    <published>2020-03-26T08:56:46.000Z</published>
    <updated>2025-10-12T14:54:52.367Z</updated>
    
    <content type="html"><![CDATA[<p>近日我把个人博客从 WordPress 迁到了 Hexo，迁移的原因有两点：</p><ul><li>WordPress 是动态框架，每次运行都要动态生成内容（即使有缓存插件还是需要运行 PHP 脚本），不仅让网站访问的速度降低了，而且我觉得这种可以通过用户名和密码认证来直接在 Web 页面上获取网站管理权限的机制，不如直接部署 HTML 静态页面安全（毕竟静态页面不用运行 PHP 代码）；</li><li>WordPress 在写文章的时候只能开着浏览器在线上写，文章数据也只能保存在服务器中，对于之前由于服务器无法登录而损失了宝贵数据的我来说，文章数据还是放在本地更加放心。另外，将文章从 WordPress 导出来比较困难，平时写的一些文章我也会将它们发布到简书、公众号上等，没有文章的 Markdown 文件是比较麻烦的一件事。</li></ul><p>综上所述，既能使用静态文件生成网站，又能为我在本地保留 Markdown 文件的个人博客框架，那就首选 Hexo 了，至于为什么不选其他的静态框架，是因为我同学的博客也是用 Hexo 搭建的，所以我对这个比较熟悉。</p><p>下面就开始讲如何使用 Hexo 搭建个人博客。（Hexo 官方网站：<a href="/link/?t=862SPB" target="_blank">https://hexo.io/zh-cn/</a>）</p><h2 id="一、架构概述"><a href="#一、架构概述" class="headerlink" title="一、架构概述"></a>一、架构概述</h2><p>我在网上看到很多使用 Hexo 搭建个人博客的教程，使用的是 Github Pages 功能，将 Hexo 生成的 HTML 上传到 GitHub 的 Repo 里，开启 Pages 功能就可以通过 Github.com 的二级域名进行访问了。这种部署方式很方便，也不需要自己有服务器和域名，适合不想折腾太多的人。</p><p>这篇文章介绍的是使用自己的服务器来进行部署，以及使用 Github 的 Webhooks 自动更新服务器上的静态资源，Github 只是一个文件的中转站和备份，这么做的原因是我还有其他的服务挂在域名上，而那些服务需要 PHP 运行环境。</p><p>大概的架构图为：</p><p><img src="/img/hexo-website-arch.jpg" alt="网站架构图"></p><p>在文章更新的时候，大概流程是：</p><ul><li>本地计算机执行 git push</li><li>Github 触发 push 事件，启动 Webhook，将事件信息通过 HTTP POST 发送给服务器上的特定路径的 PHP 脚本</li><li>脚本运行 git pull 命令，更新 Hexo 资源目录</li></ul><h2 id="二、安装-Hexo"><a href="#二、安装-Hexo" class="headerlink" title="二、安装 Hexo"></a>二、安装 Hexo</h2><p>使用 Hexo 搭建个人博客的第一步当然是安装 Hexo 了，在本地安装 Hexo 需要先安装 Node.js 运行环境，Hexo 使用 npm 包管理器进行安装。</p><h3 id="1-安装-Node-js"><a href="#1-安装-Node-js" class="headerlink" title="1.安装 Node.js"></a>1.安装 Node.js</h3><p>Node.js 官网地址为：<a href="/link/?t=J33V49" target="_blank">https://nodejs.org/zh-cn/</a>，进入官网后建议选择【长期支持版】进行下载，下载后跟随安装界面一路下去即可。如果下载速度太慢，可以使用淘宝 npm 镜像下载，地址为：<a href="/link/?t=253OK1" target="_blank">https://npm.taobao.org/mirrors/node</a></p><p><img src="/img/hexo-nodejs-website.png"></p><p>安装过程完成后，可以在命令行执行</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs powershell">node <span class="hljs-literal">--version</span><br></code></pre></td></tr></table></figure><p>查看是否可以显示 Node.js 的版本号，如果显示就说明安装成功了。</p><h3 id="2-安装-Hexo"><a href="#2-安装-Hexo" class="headerlink" title="2.安装 Hexo"></a>2.安装 Hexo</h3><p>安装 Node.js 后，就是安装 Hexo 框架了。打开命令行（Cmd 或 PowerShell）执行命令：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs powershell"><span class="hljs-comment"># 安装Hexo-cli，Hexo本体</span><br>npm install hexo<span class="hljs-literal">-cli</span> <span class="hljs-literal">-g</span><br></code></pre></td></tr></table></figure><p>等待其运行成功后，执行</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs powershell">hexo <span class="hljs-literal">-v</span><br></code></pre></td></tr></table></figure><p>出现如下输出即为安装成功</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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><code class="hljs powershell">hexo<span class="hljs-literal">-cli</span>: <span class="hljs-number">3.1</span>.<span class="hljs-number">0</span><br>os: Windows_NT <span class="hljs-number">10.0</span>.<span class="hljs-number">18363</span> win32 x64<br>node: <span class="hljs-number">12.13</span>.<span class="hljs-number">0</span><br>v8: <span class="hljs-number">7.7</span>.<span class="hljs-number">299.13</span><span class="hljs-literal">-node</span>.<span class="hljs-number">12</span><br>uv: <span class="hljs-number">1.32</span>.<span class="hljs-number">0</span><br>zlib: <span class="hljs-number">1.2</span>.<span class="hljs-number">11</span><br>brotli: <span class="hljs-number">1.0</span>.<span class="hljs-number">7</span><br>ares: <span class="hljs-number">1.15</span>.<span class="hljs-number">0</span><br>modules: <span class="hljs-number">72</span><br>nghttp2: <span class="hljs-number">1.39</span>.<span class="hljs-number">2</span><br>napi: <span class="hljs-number">5</span><br>llhttp: <span class="hljs-number">1.1</span>.<span class="hljs-number">4</span><br>http_parser: <span class="hljs-number">2.8</span>.<span class="hljs-number">0</span><br>openssl: <span class="hljs-number">1.1</span>.<span class="hljs-number">1</span>d<br>cldr: <span class="hljs-number">35.1</span><br>icu: <span class="hljs-number">64.2</span><br>tz: <span class="hljs-number">2019</span>a<br>unicode: <span class="hljs-number">12.1</span><br></code></pre></td></tr></table></figure><h3 id="3-安装-Git"><a href="#3-安装-Git" class="headerlink" title="3.安装 Git"></a>3.安装 Git</h3><p>现在来安装 Git，用于网站生成完成后的部署。</p><p>首先下载 Git 安装程序：<a href="/link/?t=WKF7E2" target="_blank">https://git-scm.com/downloads</a>，选择符合自己的平台下载即可，如果下载速度太慢可以使用淘宝镜像：<a href="/link/?t=25YPDZ" target="_blank">https://npm.taobao.org/mirrors/git-for-windows/</a>（或者复制链接使用迅雷下载，我试了一下还挺快）。</p><p>下载完成后运行程序，如果有 Add Git to Path 的选项请勾选。然后打开命令行（CMD 或者 PowerShell）输入：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs powershell">git <span class="hljs-literal">--version</span><br></code></pre></td></tr></table></figure><p>显示版本即安装成功。</p><h2 id="三、创建-Git-Repository"><a href="#三、创建-Git-Repository" class="headerlink" title="三、创建 Git Repository"></a>三、创建 Git Repository</h2><p>我们使用 Github 来作为网站的备份和版本控制，这里我们将创建两个 Repository，一个用来放置网站的源代码，对于 Hexo 来说就是 Markdown 文件和各种配置文件。另一个用来放置 Hexo 生成的网页内容，并设置 Webhook 和我们的服务器保持同步。</p><figure style="border-radius: 3px; padding: 1rem; background: rgb(235,236,237); white-space:pre-wrap;display:flex" id="8dc5c6b0-cca5-4cc9-a52a-0ca39c678b3e"><div style="font-size:1.5em"><span class="icon">💡</span></div><div style="width:100%"><p>针对没有使用过 Github 的同学，需要先设置 SSH Key，点击 Github 右上角的头像，点击 Settings 进入设置，选择左侧的 SSH and GPG Keys，然后点击右边的绿色的 New SSH Key 按钮。然后打开 PowerShell，输入命令</p><pre><code class="powershell">ssh-keygen -t rsa</code></pre><p>然后一路回车即可，就会在系统盘你的用户目录下的 .ssh 目录多出来一个 <code>id_rsa</code> 文件，这是你的私钥，另外会有一份 <code>id_rsa.pub</code> 文件，这是你的公钥，使用笔记本打开 pub 文件，复制里面的所有内容。然后在 Github 刚才打开的页面中把复制的内容添加到 Key 一栏中，并写一个Title 点击 Add SSH Key 按钮即可。</p></div></figure><p>首先创建第一个用于托管源文件的 Repository。</p><p>登录 Github，创建一个新的 Repository，设置权限为 Private（仅自己可见），这里命名为“Hexo-Source”，并初始化一个 Readme.md 文件。如下图：</p><p><img src="/img/hexo-github-new-repo.png"></p><p>相似的，我们再创建一个新的 Repository 用来托管网站生成后的文件，这里命名为“Hexo-Website”，同样也设置为 Private。</p><p>现在我们就有了两个 Github Repository，接下来就是生成网站的步骤了。</p><h2 id="四、创建-Hexo-项目"><a href="#四、创建-Hexo-项目" class="headerlink" title="四、创建 Hexo 项目"></a>四、创建 Hexo 项目</h2><p>首先要把 Github 上的 Hexo-Source Repository 克隆下来。</p><p>然后我们打开 PowerShell，使用 CD 命令进入想要存储刚才克隆的 Repository 目录，执行下面的代码进行项目的初始化：</p><figure class="highlight powershell"><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><code class="hljs powershell">hexo init &lt;folder&gt;<br><span class="hljs-built_in">cd</span> &lt;folder&gt;<br>npm install<br></code></pre></td></tr></table></figure><p>其中的  <code>&lt;folder&gt;</code> 替换为你的项目名称。</p><p>初始化完成以后的目录结构是这样的：</p><figure class="highlight 1c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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><code class="hljs 1c">.<br>├── _config.yml<br>├── package.json<br>├── scaffolds<br>├── source<br><span class="hljs-string">|   ├── _drafts</span><br><span class="hljs-string">|   └── _posts</span><br>└── themes<br></code></pre></td></tr></table></figure><ul><li>_config.yml - 项目的配置文件（主要需要修改的配置文件）</li><li>package.json - 应用程序的信息（一般不用修改）</li><li>scaffolds - 新建文章时程序使用的模板</li><li>source - 存储文章 Markdown 源码的目录<ul><li>_drafts - 文章草稿源码</li><li>_posts - 发布的文章源码</li></ul></li><li>themes - 主题目录（将自己想用的主题文件放在此文件夹中）</li></ul><p>关于各种目录具体的用途，请参考官网文档：<a href="/link/?t=LLM338" target="_blank">https://hexo.io/zh-cn/docs/setup</a></p><h2 id="五、配置网站"><a href="#五、配置网站" class="headerlink" title="五、配置网站"></a>五、配置网站</h2><p>创建项目后，这只是一个默认的模板，我们需要在网站中添加自己的信息，修改网站的主题等，使得它更像“自己的个人博客”。</p><p>首先需要修改的是 <code>_config.yml</code> 文件，关于这个文件的具体配置项，请参考文档：<a href="/link/?t=AP7YIX" target="_blank">https://hexo.io/zh-cn/docs/configuration</a>，下面简要介绍一下常用的配置。</p><table><thead><tr><th>配置项名称</th><th>用途</th><th>常用值</th></tr></thead><tbody><tr><td>title</td><td>网站名称，即显示在浏览器标题栏中的名称。</td><td>你的网站名称</td></tr><tr><td>subtitle</td><td>网站副标题，会被一些主题使用，显示在主页上。</td><td>你的副标题</td></tr><tr><td>description</td><td>网站描述，用于搜索引擎或者爬虫等。</td><td>你的网站描述</td></tr><tr><td>keywords</td><td>概括网站内容的关键词，用于搜索引擎或者爬虫等，使用逗号分隔。</td><td>你的关键词。</td></tr><tr><td>author</td><td>作者名称，也就是你的名称（昵称）。</td><td>你的名称。</td></tr><tr><td>language</td><td>网站语言，网站生成后的一些通用功能显示的语言。</td><td>zh-Hans</td></tr><tr><td>timezone</td><td>网站的时区。</td><td>‘Asia&#x2F;Shanghai’</td></tr><tr><td>url</td><td>你的网站所部署的路径地址，例如我的网站就是 <code>https://maphical.cn</code>，支持子目录，例如 <code>https://maphical.cn/blog</code></td><td>你所计划的网站访问地址</td></tr><tr><td>root</td><td>你的网站根目录的路径。</td><td>&#x2F;</td></tr><tr><td>permalink</td><td>博客文章的永久链接格式，可查阅文档了解构造方式。</td><td>:year&#x2F;:month&#x2F;:title&#x2F;</td></tr><tr><td>date_format</td><td>日期显示格式。</td><td>YYYY-MM-DD</td></tr><tr><td>time_format</td><td>时间显示格式。</td><td>HH:mm:ss</td></tr><tr><td>theme</td><td>主题名称，填写 <code>theme</code> 目录下的主题目录的名称，可以自己下载主题放入此目录中。</td><td>你的主题目录名称</td></tr><tr><td>plugins（非必要修改）</td><td>网站所使用的插件。</td><td>插件名称</td></tr><tr><td>feed（非必要修改）</td><td>这项是使用了 <code>hexo-generator-feed</code> 插件后才需要指定的，用于生成网站的 RSS 源。</td><td>有三项配置：type、path 和 limit，请查阅插件文档</td></tr><tr><td>deploy</td><td>用于部署网站。</td><td>稍后我们会进行配置</td></tr></tbody></table><p>下面我们进行主题的配置，我们可以先在 Hexo 的主题网站中寻找一个自己喜欢的主题：<a href="/link/?t=SWXN47" target="_blank">https://hexo.io/themes/</a>。</p><p>找到之后，点击主题名称，进入主题代码托管的平台，然后将其下载下来，解压在 <code>themes</code> 目录中，主题文件的根目录必须在 <code>themes/&lt;theme-name&gt;</code> 中。</p><p>接下来就需要修改主题的配置文件，即主题目录中的 <code>_config.yml</code> 文件。不同主题提供的功能不同，所需要配置的选项也不同，所以请参考所选主题的文档进行操作（一般在主题代码托管的平台就可找到）。</p><h2 id="六、如何写文章"><a href="#六、如何写文章" class="headerlink" title="六、如何写文章"></a>六、如何写文章</h2><p>Hexo 写作需要使用 Markdown，对 Markdown 不熟悉的读者可以参考：<a href="/link/?t=SFTDZR" target="_blank">https://www.runoob.com/markdown/md-tutorial.html</a>。</p><p>Hexo 的文章常用的类别为已发布的文章（Post）和草稿（Draft）。我们在创建文章的时候，两种不同的类别有不同的命令，这两种文章的区别是：</p><ul><li>草稿（Draft）的 Markdown 文件存储在项目根目录的 <code>source/_drafts</code> 目录中，在生成网站时不会将草稿包括在内，即草稿不会出现在我们的网站中。</li><li>已发布的文章（Post）的 Markdown 文件存储在项目根目录的 <code>source/_posts</code> 目录中，在生成网站的时会将此目录中的所有文章包括在内，即我们可以在网站中看到我们发布的文章。</li></ul><p>创建草稿的命令：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs powershell">hexo new draft &lt;title&gt;<br></code></pre></td></tr></table></figure><p>创建已发布文章的命令：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs powershell">hexo new post &lt;title&gt;<br></code></pre></td></tr></table></figure><p>将草稿发布的命令（将草稿转换为已发布的文章）：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs powershell">hexo publish &lt;title&gt;<br></code></pre></td></tr></table></figure><p>还有一种文章类型叫做“页面（Page）”，页面是网站中单独的网页，不属于博客文章列表中，不会在首页中显示出来。创建页面的命令为：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs powershell">hexo new page &lt;title&gt;<br></code></pre></td></tr></table></figure><p>和上面两种文章类型不同，页面生成后会在 <code>source</code> 目录中创建一个以页面标题命名的文件夹，文件夹里面自动生成一个名为 <code>index.md</code> 的文件，编辑这个文件即可编辑页面的内容。</p><p>还记得这个目录是我们克隆下来的吗？编辑好文章内容后，可以将此项目 Push 一下，托管到 Github（处于备份目的）。</p><h2 id="七、预览网站效果"><a href="#七、预览网站效果" class="headerlink" title="七、预览网站效果"></a>七、预览网站效果</h2><p>写好了自己的文章后，我们就可以在本地进行网站效果的预览了，通过启动本地 Hexo 服务器可以进行预览。</p><p>首先安装服务器模块（在 Hexo 项目根目录）：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs powershell">npm install hexo<span class="hljs-literal">-server</span> <span class="hljs-literal">--save</span><br></code></pre></td></tr></table></figure><p>安装完成后，执行 generate 命令生成网站代码：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs powershell">hexo generate<br></code></pre></td></tr></table></figure><p>然后启动服务器：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs powershell">hexo server<br></code></pre></td></tr></table></figure><p>然后我们就可以在 <a href="http://localhost:4000/">http://localhost:4000</a> 这个地址访问我们的网站了。</p><p>如果发现需要修改内容，直接修改文件即可，修改完成后不需要重启服务器，更改会自动应用。若启动服务器时发生了 <code>EADDRINUSE</code>（端口被占用）错误，可以使用 <code>-p</code> 参数指定启动端口。</p><h2 id="八、部署网站"><a href="#八、部署网站" class="headerlink" title="八、部署网站"></a>八、部署网站</h2><p>可以使用 Github Pages 托管自己的网站，如果使用这种方式，请参考文档：<a href="/link/?t=ITDSCF" target="_blank">https://hexo.io/zh-cn/docs/github-pages</a>。</p><p>下面要介绍的是通过服务器的部署方式。</p><p>首先，我们需要安装 Hexo 的 Git 部署模块，在 Hexo 项目根目录执行：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs powershell">npm install hexo<span class="hljs-literal">-deployer-git</span> <span class="hljs-literal">--save</span><br></code></pre></td></tr></table></figure><p>安装完成后，打开项目根目录的 <code>_config.yml</code> 文件，找到 deploy 选项，按照下面的内容进行修改：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-comment"># Deployment</span><br><span class="hljs-comment">## Docs: https://hexo.io/docs/deployment.html</span><br><span class="hljs-attr">deploy:</span><br>  <span class="hljs-attr">type:</span> <span class="hljs-string">git</span><br>  <span class="hljs-attr">repo:</span> <span class="hljs-string">&lt;你的github中用来托管网站的项目的ssh地址，即我们上文中创建的Hexo-Website</span> <span class="hljs-string">Repository&gt;</span><br>  <span class="hljs-attr">branch:</span> <span class="hljs-string">master</span><br>  <span class="hljs-attr">message:</span> <span class="hljs-string">&#x27;你想写的提交信息&#x27;</span><br></code></pre></td></tr></table></figure><p>这一步告诉 Hexo 我们想要使用 Git 进行部署，且提供了 Git 项目的地址等信息。</p><p>然后就可以进行上传了，在根目录执行命令：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs powershell">hexo deploy<br></code></pre></td></tr></table></figure><p>等待执行完毕即可。</p><p>那么现在就要开始配置服务器了，登录自己的服务器，我以 CentOS 系统为例进行说明。</p><ul><li>首先确保服务器中安装了 Git，然后创建一个新用户，这里且命名为 <code>hexo</code>，修改 <code>/etc/passwd</code> 文件授予此用户执行 Bash 脚本的权限。然后我们切换到这个用户：<code>su hexo</code>。</li><li>在 <code>/var</code> 目录下创建一个新目录，这里命名为 <code>hexo_blog</code>。进入此目录，使用 git 把 Hexo-Website Repository 克隆下来，那么克隆成功后的目录就是 <code>Hexo-Website</code>。</li><li>安装 Nginx，将 Nginx 的运行用户修改为 hexo（在 <code>/etc/nginx/nginx.conf</code> 中修改）。</li><li>配置 <code>nginx.conf</code>，将其 server 块中的内容替换为：</li></ul><figure class="highlight nginx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><code class="hljs nginx"><span class="hljs-attribute">listen</span>       <span class="hljs-number">80</span> http2 default_server;<br><span class="hljs-attribute">listen</span>       [::]:<span class="hljs-number">80</span> http2 default_server;<br><span class="hljs-attribute">server_name</span>  你的域名;<br><span class="hljs-attribute">root</span>         /var/hexo_blog/Hexo-Website;<br><span class="hljs-attribute">access_log</span> /var/log/nginx/access-hexo.log;<br><span class="hljs-attribute">error_log</span> /var/log/nginx/<span class="hljs-literal">error</span>-hexo.log;<br><br><span class="hljs-attribute">index</span> index.html index.php;<br><br><span class="hljs-section">location</span> = /favicon.ico &#123;<br><span class="hljs-attribute">log_not_found</span> <span class="hljs-literal">off</span>;<br><span class="hljs-attribute">access_log</span> <span class="hljs-literal">off</span>;<br>&#125;<br><br><span class="hljs-section">location</span> = /robots.txt &#123;<br><span class="hljs-attribute">allow</span> all;<br><span class="hljs-attribute">log_not_found</span> <span class="hljs-literal">off</span>;<br><span class="hljs-attribute">access_log</span> <span class="hljs-literal">off</span>;<br>&#125;<br><br><span class="hljs-section">location</span> / &#123;<br>&#125;<br><br><span class="hljs-section">location</span> <span class="hljs-regexp">~ \.php$</span> &#123;<br><span class="hljs-comment">#<span class="hljs-doctag">NOTE:</span> You should have &quot;cgi.fix_pathinfo = 0;&quot; in php.ini</span><br><span class="hljs-attribute">include</span> fastcgi.conf;<br><span class="hljs-attribute">fastcgi_param</span> SCRIPT_FILENAME <span class="hljs-variable">$document_root</span><span class="hljs-variable">$fastcgi_script_name</span>;<br><span class="hljs-attribute">fastcgi_intercept_errors</span> <span class="hljs-literal">on</span>;<br><span class="hljs-attribute">fastcgi_pass</span> <span class="hljs-number">127.0.0.1:9000</span>;<br>&#125;<br><br><span class="hljs-section">location</span> <span class="hljs-regexp">~* \.(js|css|png|jpg|jpeg|gif|ico)$</span> &#123;<br><span class="hljs-attribute">expires</span> max;<br><span class="hljs-attribute">log_not_found</span> <span class="hljs-literal">off</span>;<br>&#125;<br><br><span class="hljs-attribute">error_page</span> <span class="hljs-number">404</span> /<span class="hljs-number">404</span>.html;<br></code></pre></td></tr></table></figure><ul><li>然后重启 Nginx 即可访问网站（这里我省略了申请域名和配置域名解析的内容）。</li></ul><p>接下来，设置自动更新。</p><ul><li>首先安装 PHP 运行环境：php-fpm，安装完成后启动，并设置监听端口为 9000.</li><li>在 <code>/var/hexo_blog/Hexo-Website</code> 中创建一个目录，命名为 <code>api</code>，创建一个名为 <code>sync.php</code> 的 PHP 文件，内容如下：</li></ul><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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><code class="hljs php"><span class="hljs-meta">&lt;?php</span><br><br><span class="hljs-comment">// 认证字符串，需要填入Github Webhook Secret中</span><br><span class="hljs-variable">$code</span> = <span class="hljs-string">&#x27;xxxxxxxxxxxxxxxx&#x27;</span>;<br><br><span class="hljs-comment">// 测试认证</span><br><span class="hljs-keyword">if</span> (!<span class="hljs-keyword">isset</span>(<span class="hljs-variable">$_GET</span>[<span class="hljs-string">&#x27;code&#x27;</span>]) || <span class="hljs-variable">$_GET</span>[<span class="hljs-string">&#x27;code&#x27;</span>] !== <span class="hljs-variable">$code</span>)<br>&#123;<br>    <span class="hljs-keyword">exit</span>(<span class="hljs-string">&#x27;请提供正确的认证代码。&#x27;</span>);<br>&#125;<br><br><span class="hljs-comment">// 计算Github HMAC认证</span><br><span class="hljs-variable">$data_body</span> = <span class="hljs-title function_ invoke__">file_get_contents</span>(<span class="hljs-string">&#x27;php://input&#x27;</span>);<br><span class="hljs-variable">$hash</span> = <span class="hljs-title function_ invoke__">hash_hmac</span>(<span class="hljs-string">&#x27;sha1&#x27;</span>, <span class="hljs-variable">$data_body</span>, <span class="hljs-variable">$code</span>);<br><span class="hljs-variable">$hash</span> = <span class="hljs-string">&#x27;sha1=&#x27;</span> . <span class="hljs-variable">$hash</span>;<br><span class="hljs-keyword">if</span> (!<span class="hljs-title function_ invoke__">function_exists</span>(<span class="hljs-string">&#x27;getallheaders&#x27;</span>)) &#123;<br>    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getallheaders</span>(<span class="hljs-params"></span>) </span>&#123;<br>        <span class="hljs-keyword">foreach</span> (<span class="hljs-variable">$_SERVER</span> <span class="hljs-keyword">as</span> <span class="hljs-variable">$name</span> =&gt; <span class="hljs-variable">$value</span>) &#123;<br>            <span class="hljs-keyword">if</span> (<span class="hljs-title function_ invoke__">substr</span>(<span class="hljs-variable">$name</span>, <span class="hljs-number">0</span>, <span class="hljs-number">5</span>) == <span class="hljs-string">&#x27;HTTP_&#x27;</span>) &#123;<br>                <span class="hljs-variable">$headers</span>[<span class="hljs-title function_ invoke__">str_replace</span>(<span class="hljs-string">&#x27; &#x27;</span>, <span class="hljs-string">&#x27;-&#x27;</span>, <span class="hljs-title function_ invoke__">ucwords</span>(<span class="hljs-title function_ invoke__">strtolower</span>(<span class="hljs-title function_ invoke__">str_replace</span>(<span class="hljs-string">&#x27;_&#x27;</span>, <span class="hljs-string">&#x27; &#x27;</span>, <span class="hljs-title function_ invoke__">substr</span>(<span class="hljs-variable">$name</span>, <span class="hljs-number">5</span>)))))] = <span class="hljs-variable">$value</span>;<br>            &#125;<br>        &#125;<br>        <span class="hljs-keyword">return</span> <span class="hljs-variable">$headers</span>;<br>    &#125;<br>&#125;<br><span class="hljs-keyword">if</span> (!<span class="hljs-keyword">isset</span>(<span class="hljs-title function_ invoke__">getallheaders</span>()[<span class="hljs-string">&#x27;X-Hub-Signature&#x27;</span>]))<br>&#123;<br>    <span class="hljs-keyword">exit</span>(<span class="hljs-string">&#x27;HMAC认证失败，无签名。&#x27;</span>);<br>&#125;<br><span class="hljs-variable">$signature</span> = <span class="hljs-title function_ invoke__">getallheaders</span>()[<span class="hljs-string">&#x27;X-Hub-Signature&#x27;</span>];<br><span class="hljs-keyword">if</span> (<span class="hljs-variable">$hash</span> !== <span class="hljs-variable">$signature</span>)<br>&#123;<br>    <span class="hljs-keyword">exit</span>(<span class="hljs-string">&#x27;HMAC认证失败。&#x27;</span>);<br>&#125;<br><br><span class="hljs-variable">$command</span> = <span class="hljs-string">&#x27;cd /var/hexo_blog/crontab &amp;&amp; nohup ./personal_hexo_blog_hook_sync.sh &amp;&#x27;</span>;<br><br><span class="hljs-comment">// 执行更新脚本</span><br><span class="hljs-title function_ invoke__">system</span>(<span class="hljs-variable">$command</span>, <span class="hljs-variable">$status</span>);<br><span class="hljs-keyword">if</span> (<span class="hljs-variable">$status</span>)<br>&#123;<br>    <span class="hljs-keyword">echo</span> <span class="hljs-string">&#x27;执行失败。&#x27;</span>;<br>&#125;<br><span class="hljs-keyword">else</span><br>&#123;<br>    <span class="hljs-keyword">echo</span> <span class="hljs-string">&#x27;执行成功。&#x27;</span>;<br>&#125;<br></code></pre></td></tr></table></figure><ul><li>上面代码中的 <code>xxxxxxxxxxxxxxxx</code> 需要换成你自己指定的认证码（就是随意的一串字符串），这个认证码下面还会用到。</li><li>创建目录 <code>/var/hexo_blog/crontab</code>，在其中创建一个名为 <code>personal_hexo_blog_hook_sync.sh</code> 的文件，其内容为：</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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><code class="hljs bash"><span class="hljs-meta">#!/bin/bash</span><br><br><span class="hljs-comment"># 用于响应Github hook拉取Github的更新</span><br><br><span class="hljs-comment"># 记录日志</span><br><span class="hljs-built_in">echo</span> $(<span class="hljs-built_in">date</span>)<span class="hljs-string">&quot;: Git-Work(Hexo)&quot;</span> &gt;&gt; /var/hexo_blog/git_hook_sync.log<br><br><span class="hljs-built_in">cd</span> /var/hexo_blog/Hexo-Website<br>git pull &gt;&gt; /var/hexo_blog/git_hook_sync.log<br><br><span class="hljs-built_in">echo</span> <span class="hljs-string">&quot;End=====================&quot;</span> &gt;&gt; /var/hexo_blog/git_hook_sync.log<br></code></pre></td></tr></table></figure><ul><li>给 <code>personal_hexo_blog_hook_sync.sh</code> 文件加上可执行权限。</li><li>编辑 php-fpm 的配置文件，让其以 hexo 用户运行。</li><li>重启 php-fpm 服务。</li></ul><p>现在，我们的服务器已经配置完成。</p><p>我们进入 Github 刚才创建的 Hexo-Website Repository，进入项目的 Settings 选项，点击左侧的 Webhooks，再点击右侧的 Add Webhook 添加新的钩子：</p><p><img src="/img/hexo-add-webhook.png"></p><p>图中，将 <code>你的域名</code> 换成你的域名地址，将 <code>xxxxxxxxxxxxxxxx</code> 换成上文中我们 PHP 代码中设置的认证码，Content type 设置为 <code>application/json</code>，Secret 中也填写相同的认证码，其他的按照图中设置即可。</p><p>然后点击 Add Webhook 即可成功添加钩子。</p><h2 id="九、测试自动更新"><a href="#九、测试自动更新" class="headerlink" title="九、测试自动更新"></a>九、测试自动更新</h2><p>现在我们就有了一个可以自动更新的 Hexo 个人网站了，是不是很简单（bushi）！</p><p>我们来测试一下自动更新功能：</p><ul><li>修改或者增添一篇博客文章（在本地计算机）。</li><li>运行命令（在本地计算机）：</li></ul><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs powershell">hexo clean &amp;&amp; hexo generate &amp;&amp; hexo deploy<br></code></pre></td></tr></table></figure><ul><li>等待运行完成，然后再等待大概 2-3 分钟时间让服务器完成更新。</li><li>访问你的域名，查看你刚才修改的博客文章是否已经更新了！</li></ul><p>要查看 Github 的 Webhook 运行是否正常，进入 Repository 的 Webhook 界面，点击我们添加的 Webhook，往下翻就能看到最近进行过的 Hook 活动了，点击项目就能看到 Github 在执行 Hook 的时候服务器的返回状态码和返回内容，由此判断可能出现的问题。</p><p><img src="/img/hexo-check-webhook.png"></p><p>如上，Body 里返回了 <code>执行成功。</code> 则表明更新成功了。</p><p>以上。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;近日我把个人博客从 WordPress 迁到了 Hexo，迁移的原因有两点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;WordPress 是动态框架，每次运行都要动态生成内容（即使有缓存插件还是需要运行 PHP 脚本），不仅让网站访问的速度降低了，而且我觉得这种可以通过用户名和密码认证来</summary>
      
    
    
    
    <category term="Computer Science" scheme="https://maphical.cn/categories/Computer-Science/"/>
    
    <category term="Frameworks &amp; Tools" scheme="https://maphical.cn/categories/Computer-Science/Frameworks-Tools/"/>
    
    <category term="Hexo" scheme="https://maphical.cn/categories/Computer-Science/Frameworks-Tools/Hexo/"/>
    
    
  </entry>
  
  <entry>
    <title>【视频】碎片化阅读会让我们变傻么</title>
    <link href="https://maphical.cn/2020/02/video-about-fragmented-reading/"/>
    <id>https://maphical.cn/2020/02/video-about-fragmented-reading/</id>
    <published>2020-02-15T15:39:25.000Z</published>
    <updated>2025-10-12T14:54:46.495Z</updated>
    
    <content type="html"><![CDATA[<p>这是我在大三时参加多媒体作品设计与开发课程的小组作业的作品，由小组成员一起设计和制作。视频主要讲述的是长时间的【碎片化阅读】对我们思维和生活的影响，主要从：1.什么是碎片化阅读；2.为什么长期的碎片化阅读会对我们产生影响；3.如何降低碎片化阅读带给我们的消极影响。</p><p><code>&lt;span style=&quot;color:grey&quot;&gt;</code><em>注：本视频中的观点来自我们在作业中所查阅的相关论文资料，这里只是进行一个简单的概括和总结。我们并非对此领域有专业深入的研究和认识，只是提供一个了解碎片化阅读的角度。</em></p><p><strong>B站视频链接：</strong><a href="/link/?t=825LOU" target="_blank">av50436230</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;这是我在大三时参加多媒体作品设计与开发课程的小组作业的作品，由小组成员一起设计和制作。视频主要讲述的是长时间的【碎片化阅读】对我们思维和生活的影响，主要从：1.什么是碎片化阅读；2.为什么长期的碎片化阅读会对我们产生影响；3.如何降低碎片化阅读带给我们的消极影响。&lt;/p&gt;
</summary>
      
    
    
    
    <category term="My Video" scheme="https://maphical.cn/categories/My-Video/"/>
    
    
  </entry>
  
  <entry>
    <title>记一次失败的网站设计和开发</title>
    <link href="https://maphical.cn/2019/08/experience-of-a-fail-website-design/"/>
    <id>https://maphical.cn/2019/08/experience-of-a-fail-website-design/</id>
    <published>2019-08-07T02:49:32.000Z</published>
    <updated>2025-10-11T16:10:29.448Z</updated>
    
    <content type="html"><![CDATA[<p>有一定规模的软件开发需要多人合作才能实现，团队中的每个人都负责不同的工作，完成各自的工作后进行对接，调试系统使得各个由不同人员开发的模块可以协调工作并满足相关的要求，最终形成一个完整健壮的软件系统。</p><p>以我在学校中做项目的经验来看（特别是与网络相关的项目），在整个开发流程中最关键的点之一就是系统的代码拆分，即将系统分解形成不同的模块分发给不同的团队成员去实现。系统拆分需要从系统功能性需求和非功能性需求两方面同时考虑，例如在工作量正常的情况下，与用户系统相关的功能设计最好放在同一个模块中实现，而不应该将用户注册和用户登录分为两个模块，甚至交给两组不同的团队去实现。</p><h3 id="一、背景"><a href="#一、背景" class="headerlink" title="一、背景"></a>一、背景</h3><p>在最近的软件工程课程设计中，我们组被分配到的任务是“校园二手书”平台网站的设计与实现，不要求特定语言与框架。在经过组员之间的讨论后，我和另外一个负责开发的组员决定<strong>使用 Python Django 框架开发后端，使用 Bootstrap 开发前端，前后端之间使用 Javascript jQuery 进行异步数据交互，并且<span style='color:red'>前后端分离</span></strong>。</p><p>至于为什么只有两名开发组员，是因为这次的课设工作量并不大，且功能并没有很繁多，只是简单地实现一个较小规模的网站平台。另外，在一开始我们也曾考虑过使用 Java Web 技术进行开发，但由于想要尝试并学习一下新的框架（玩具），我们才放弃了最熟悉的 Java 技术栈转向并没有开发经验的 Python Django，同时，作为前端开发的我也并没有能拿得出手的前端开发项目经验。更加棘手的是，课设的那一个星期正赶上移动应用程序和云计算课程的结课，软工课设时间被挤压只能在三天内从零做完整个项目（原计划是两个星期）。</p><h3 id="二、开发过程"><a href="#二、开发过程" class="headerlink" title="二、开发过程"></a>二、开发过程</h3><p>在开发的前期，因为前后端分离的原因，我们两个人除了交流功能、讨论界面设计和确定数据格式以外，基本没有其他的合作，各自确定代码结构和实现。在开发了一段时间之后，对完成的首页进行测试，发现由于 JavaScript 进行了跨域访问，数据无法提交，只好将前端部署到服务器上进行访问，这样就造成了代码修改的不便，需要进行频繁的部署和访问。由于时间紧迫，没有使用 Webhook 等自动部署工具。</p><p>解决了跨域访问的问题后，由于使用网页端 JavaScript 异步登录，不知什么原因在进行登录后服务器端无法识别 Session（可能和 Cookie 相关），导致登录始终失败无法进行下一步的测试。</p><p>除了上述两个问题外，因为前后端分离，我们试图使用原生 JavaScript 通过异步数据交换，对 DOM 进行操作，更新网页内容。由于前端知识掌握不足，模板等技术不会使用，只好利用字符串拼接在 JavaScript 中形成 HTML 代码，从而更新内容。这种方法浪费了大量的时间（因为有大量的字符串和变量拼接，以及字符转义操作等），且效果非常不理想，JS 代码可读性较差，查找 Bug 困难，且不易修改。</p><p>终于，在积累了大量的 Bug 后，我们没能在规定时间内解决，无法完成一个功能正常的网站，作品存在的问题如下：</p><ul><li>登录始终不成功</li><li>数据交换成功率不高</li><li>页面内容更新效果差，失败率高</li><li>页面美观性低</li></ul><h3 id="三、解决方案"><a href="#三、解决方案" class="headerlink" title="三、解决方案"></a>三、解决方案</h3><p>经过和老师的沟通，老师同意让我们延后一周提交作品。我们决定放弃这种方案，放弃前后端分离的设计，使用 Django 的模板进行开发。这样一来有点如下：</p><ul><li>Session 问题自然解决，因为放弃异步登录，所有登录操作都在服务器完成</li><li>数据交换都在服务器完成</li><li>页面内容更新不需要 JS 对 DOM 进行操作，使用 HTML 模板生成</li><li>有更多精力和时间进行页面美化和功能完善</li><li>将所有业务逻辑代码放在框架中完成，提高了代码的可读性和可维护性</li></ul><p>经过3天的重构和开发，我们最终完成了具有正常功能的网站……</p><h3 id="四、总结"><a href="#四、总结" class="headerlink" title="四、总结"></a>四、总结</h3><p>我认为，这次的课程设计失败的主要原因有两个：一是在时间紧迫的情况下使用自己不熟悉的技术栈；二是在进行网站架构设计时，对原本工作量不大的代码工作进行了过度拆分，平白增加了巨大的对接工作量，耽误了完成进度。</p><p>所以，在写代码之前，首先要进行合理的软件设计，否则可能会事倍功半。</p><p>以上。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;有一定规模的软件开发需要多人合作才能实现，团队中的每个人都负责不同的工作，完成各自的工作后进行对接，调试系统使得各个由不同人员开发的模块可以协调工作并满足相关的要求，最终形成一个完整健壮的软件系统。&lt;/p&gt;
&lt;p&gt;以我在学校中做项目的经验来看（特别是与网络相关的项目），在整</summary>
      
    
    
    
    <category term="Computer Science" scheme="https://maphical.cn/categories/Computer-Science/"/>
    
    <category term="Architectures &amp; Principles" scheme="https://maphical.cn/categories/Computer-Science/Architectures-Principles/"/>
    
    <category term="Website" scheme="https://maphical.cn/categories/Computer-Science/Architectures-Principles/Website/"/>
    
    
  </entry>
  
  <entry>
    <title>搭建JAVAFX开发环境</title>
    <link href="https://maphical.cn/2019/07/setup-javafx-development-environment/"/>
    <id>https://maphical.cn/2019/07/setup-javafx-development-environment/</id>
    <published>2019-07-18T11:04:18.000Z</published>
    <updated>2025-10-12T14:54:46.495Z</updated>
    
    <content type="html"><![CDATA[<p>最近开始学习使用 JavaFX 开发桌面应用程序，但苦于入门时的环境搭建。一是对 JavaFX 和 JDK 之间的从属关系的混乱，二是对在项目中 JDK 和 JavaFX SDK 如何协同使用的不明。经过在网上寻找各种资料后，总结一篇关于如何搭建开发环境和如何使用 IDEA 配置 JavaFX 项目的文章。</p><h3 id="一、JavaFX-SDK-和-JDK-之间的关系"><a href="#一、JavaFX-SDK-和-JDK-之间的关系" class="headerlink" title="一、JavaFX SDK 和 JDK 之间的关系"></a>一、JavaFX SDK 和 JDK 之间的关系</h3><p>刚入门 JavaFX 的同学肯定对这个问题很头大（因为我就是这样的hhh），在 JDK 8 及之前版本，JavaFX 都是和 JDK 捆绑在一起的，下载安装 JDK 即可开发和使用 JavaFX 进行开发。而在 JDK 8 之后，Oracle 将 JavaFX 从 JDK 中分离了出去，作为 OpenJDK Project 的一个部分 OpenJFX Project 由社区进行运营，其官网在<a href="/link/?t=W9Z5BX" target="_blank">这里</a>。关于 JavaFX 被移除的信息请见官网<a href="/link/?t=Q6LG89" target="_blank">链接</a>。</p><p>在搭建开发环境时，如果选用 JDK 8 &#x2F; 7 的话，则不需要下载其他的 JavaFX SDK，可以直接使用 JDK 进行 JavaFX 开发；若使用 JDK 8 之后的版本，则需要安装 JDK 之外，再安装 JavaFX SDK，其下载地址见上文的官网地址。</p><h3 id="二、使用-IDEA-开发-JavaFX-项目"><a href="#二、使用-IDEA-开发-JavaFX-项目" class="headerlink" title="二、使用 IDEA 开发 JavaFX 项目"></a>二、使用 IDEA 开发 JavaFX 项目</h3><p>接下来介绍使用 IDEA 进行 JFX 开发所需要的项目配置。</p><h4 id="1-使用-JDK-8"><a href="#1-使用-JDK-8" class="headerlink" title="1.使用 JDK 8"></a>1.使用 JDK 8</h4><p>使用 JDK 8 的情况下，首先下载安装 JDK。</p><p><strong>安装完成后，打开 IDEA 并新建 JavaFX 项目：打开 File-&gt;Project Structure 窗口，选择 Project SDK-&gt;New… 选择你刚安装的 JDK 路径。</strong></p><p>接下来配置 JFX 启动参数（如果不配置启动参数，编译后会出现“无法运行 JavaFX 程序，请安装 JavaFX 运行时来运行此程序”的错误提示）：</p><p><strong>回到项目主界面，点击右上角的“播放”符号左侧的下拉菜单，选择“Edit Configurations…”选项，在跳出窗口的“VM options”项中填写下面的内容：</strong></p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs css"><span class="hljs-attr">--module-path</span> /<span class="hljs-selector-tag">path</span>/<span class="hljs-selector-tag">to</span>/javafx-sdk/lib <span class="hljs-attr">--add-modules</span> javafx<span class="hljs-selector-class">.controls</span>,javafx<span class="hljs-selector-class">.fxml</span><br></code></pre></td></tr></table></figure><p>其中“&#x2F;path&#x2F;to&#x2F;javafx-sdk&#x2F;lib”改成你安装 JavaFX SDK 的目录下 lib 目录的路径即可。</p><p>这样就配置完成了，可以使用 IDEA 开发和启动 JFX 应用程序了。</p><h4 id="2-使用-JDK-8-以后的-JDK-版本"><a href="#2-使用-JDK-8-以后的-JDK-版本" class="headerlink" title="2.使用 JDK 8 以后的 JDK 版本"></a>2.使用 JDK 8 以后的 JDK 版本</h4><p>使用 JDK 8 以后的 JDK（例如 JDK 11 等）时，同样需要进行上述所有步骤，在完成上述步骤之后，还要下载 JavaFX SDK 并安装。</p><p><strong>接着在“Project Structure”窗口选择左侧的“Libraries”分页，点击“+”号，点击“Java”，找到安装 JavaFX SDK 的目录，选择目录下的“lib”目录，点击打开即可。</strong></p><p>添加完成后点击“OK”关闭页面，即可进行 JavaFX 开发。</p><p>以上信息可以在官方网站中找到，地址为：<a href="/link/?t=RL85II" target="_blank">https://openjfx.io/openjfx-docs/#IDE-Intellij</a> ，可以将这篇博客看作以上文档的翻译和扩充。</p><p>以上。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;最近开始学习使用 JavaFX 开发桌面应用程序，但苦于入门时的环境搭建。一是对 JavaFX 和 JDK 之间的从属关系的混乱，二是对在项目中 JDK 和 JavaFX SDK 如何协同使用的不明。经过在网上寻找各种资料后，总结一篇关于如何搭建开发环境和如何使用 IDEA</summary>
      
    
    
    
    <category term="Computer Science" scheme="https://maphical.cn/categories/Computer-Science/"/>
    
    <category term="Frameworks &amp; Tools" scheme="https://maphical.cn/categories/Computer-Science/Frameworks-Tools/"/>
    
    <category term="JavaFX" scheme="https://maphical.cn/categories/Computer-Science/Frameworks-Tools/JavaFX/"/>
    
    
  </entry>
  
  <entry>
    <title>什么是CAS(COMPARE AND SWAP)</title>
    <link href="https://maphical.cn/2019/05/what-is-cas/"/>
    <id>https://maphical.cn/2019/05/what-is-cas/</id>
    <published>2019-05-01T09:17:19.000Z</published>
    <updated>2025-10-12T14:54:52.363Z</updated>
    
    <content type="html"><![CDATA[<p>CAS（Compare And Swap）是一种原子操作，用于保证在无锁情况下的数据一致性的问题。在无锁情况下，假设有两个线程 A 和 B，他们都读取某一个值 V，修改后再存回内存中，当它们并行执行时，就可能会引起数据 V 的不一致问题。</p><p>CAS 的具体操作是<strong>比较和替换</strong>，即第一步比较指定值和内存中的值是否一致，若一致则使用新值对内存值进行替换。</p><h2 id="不一致问题的举例"><a href="#不一致问题的举例" class="headerlink" title="不一致问题的举例"></a><strong>不一致问题的举例</strong></h2><p>假设有两个线程 A 和 B，它们分别对数据 V（值为100）执行加 10 和减 10 的操作，代码执行过程如下：</p><p>线程A对数据V的操作：</p><ol><li>从内存中读取数据 V（100）；</li><li>（在线程中）将数据 V加10；</li><li>将加法的结果 V1（110）存入内存中原来的位置（替换掉旧的 V）。</li></ol><p>线程 B 对数据 V 的操作：</p><ol><li>从内存中读取数据 V（100）；</li><li>（在线程中）将数据 V 减 10；</li><li>将减法的结果 V2（90）存入内存中原来的位置（替换掉旧的 V）。</li></ol><p>假设这两个线程并发执行，且 A 首先获得 CPU 时间片，在 A 的 CPU 时间内，它先读取数据 V 的值，并将其进行了加法操作，获得数据 V1（110）。此时，A 的 CPU 时间片结束，线程 B 开始执行。B 将数据 V 读入（此时数据V未被改动），并执行了减法操作，获得数据 V2（90）。此时，B 的 CPU 时间片结束，线程 A 继续执行，A 将 V1（110）存入内存，A 线程结束。B 继续执行，B 将 V2（90）存入内存，B 线程结束。</p><p>我们可以看到，此时内存中的数据 V 已经变成了 V2（90），与我们原先以为的100（加十减十）预期不同，造成了数据不一致的问题。</p><h2 id="使用CAS解决数据不一致问题"><a href="#使用CAS解决数据不一致问题" class="headerlink" title="使用CAS解决数据不一致问题"></a><strong>使用CAS解决数据不一致问题</strong></h2><p>CAS 可以用于解决上述数据不一致问题，假设线程 A 和 B 都使用了 CAS 方式，那么他们的执行步骤为：</p><p>线程 A 对数据 V 的操作：</p><ol><li>从内存中读取数据 V（100）；</li><li>（在线程中）将数据 V 加 10；</li><li>执行 CAS 操作，比较第一步读取的 V 值（100）与现在内存中的 V 值是否相等，若相等则继续；否则返回执行第一步；</li><li>将加法的结果 V1（110）存入内存中原来的位置（替换掉旧的 V）。</li></ol><p>线程B对数据V的操作：</p><ol><li>从内存中读取数据 V（100）；</li><li>（在线程中）将数据 V 减 10；</li><li>执行 CAS 操作，比较第一步读取的 V 值（100）与现在内存中的 V 值是否相等，若相等则继续；否则返回执行第一步；</li><li>将减法的结果 V2（90）存入内存中原来的位置（替换掉旧的 V）。</li></ol><p>流程修改后，在执行过程中，当 A 线程执行结束后，内存中的值已经变为 V1（110），线程B在存入新的值之前首先比较 V1 是否与 V 相同，因为内存中的值已经修改，所以线程B需要重新执行读取操作，从第一步重新执行，将 V1（110）减 10 在存入内存，得到 V（100）与预期一致，从而确保了数据的一致问题。</p><h2 id="实现"><a href="#实现" class="headerlink" title="实现"></a>实现</h2><p>CAS 操作基于 CPU 提供的原子操作指令实现。对于 Intel X86 处理器，可通过在汇编指令前增加 LOCK 前缀来锁定系统总线，使系统总线在汇编指令执行时无法访问相应的内存地址。而各个编译器根据这个特点实现了各自的原子操作函数。<a href="https://maphical.cn/2019/05/860/#n1">[1]</a></p><ul><li>C语言，C11 的头文件 stdatomic.h。由 GNU 提供了对应的 __sync 系列函数完成原子操作。</li><li>C++11，STL 提供了 atomic 系列函数。</li><li>JAVA，sun.misc.Unsafe 提供了 compareAndSwap 系列函数。</li><li>C#，通过 Interlocked 方法实现。</li><li>Go， 通过 import “sync&#x2F;atomic” 包实现。</li><li>Windows，通过 Windows API 实现了 InterlockedCompareExchangeXYZ 系列函数。</li></ul><h2 id="外部链接"><a href="#外部链接" class="headerlink" title="外部链接"></a>外部链接</h2><p>[1] 引用自 Wikipedia CAS 词条:<a href="/link/?t=3LDB7E" target="_blank">网页链接</a>。</p><p>以上。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;CAS（Compare And Swap）是一种原子操作，用于保证在无锁情况下的数据一致性的问题。在无锁情况下，假设有两个线程 A 和 B，他们都读取某一个值 V，修改后再存回内存中，当它们并行执行时，就可能会引起数据 V 的不一致问题。&lt;/p&gt;
&lt;p&gt;CAS 的具体操作是</summary>
      
    
    
    
    <category term="Computer Science" scheme="https://maphical.cn/categories/Computer-Science/"/>
    
    <category term="Theory" scheme="https://maphical.cn/categories/Computer-Science/Theory/"/>
    
    
  </entry>
  
  <entry>
    <title>搭建UNITY+VISUAL STUDIO开发环境</title>
    <link href="https://maphical.cn/2019/04/setup-unity-vs-environment/"/>
    <id>https://maphical.cn/2019/04/setup-unity-vs-environment/</id>
    <published>2019-04-25T14:55:49.000Z</published>
    <updated>2025-10-12T14:54:46.495Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/img/unity-header.jpg" alt="Unity 2019.1, Picture from Unity Blog"></p><p>Unity 是一款优秀的2D&#x2F;3D游戏引擎，使用 Unity 开发的游戏大作层出不穷。如今，Unity 已经不止用来开发游戏，其在电影、工业制造、教学、仿真实验和工程领域也有很多的应用。本文主要介绍使用 Unity 开发游戏时 Windows 平台的开发环境搭建。</p><p>Unity 开发环境主要组成部分是 Unity Editor 和脚本编辑器。Unity Editor 用于游戏中场景和对象的设计与制作，游戏中的对象称为 GameObject（游戏对象），每个游戏对象都可以添加若干 Component（组件），组件用于控制游戏对象的特征或行为，Script（脚本）就是其中一种组件，用于定义游戏对象的行为逻辑。脚本可以使用 C# 或者 Javascript 编写，我们可以使用任意的编辑器编写脚本，这里我们使用 Visual Studio 2017 为例，介绍如何安装开发环境和调试游戏对象的脚本。</p><p>此文参考了 Unity 官方的环境搭建指导视频，链接<a href="/link/?t=QM7I65" target="_blank">在此</a>（Youtube-英文版）。</p><h2 id="一、安装-Unity"><a href="#一、安装-Unity" class="headerlink" title="一、安装 Unity"></a>一、安装 Unity</h2><p>安装 Unity 推荐使用 Unity Hub 管理程序。</p><p>第一步，下载 Unity Hub。这里使用 Personal（个人版）为例，下载地址为 <a href="/link/?t=EXW0PI" target="_blank">https://store.unity.com/cn/download</a> ，下载时需要满足网页中显示的几个条件。下载安装程序后点击运行，并注册账户，完成其中内置的几个小调查即安装完毕。</p><p>第二步，安装 Unity。安装 Unity Hub 完成之后打开，选择 Installs 标签页，选择 Official Releases 页面，选择一个想要安装的版本点击 Download 即可，等待其下载完毕。</p><p><img src="/img/unity-hub.png" alt="Unity Hub Installer"></p><h2 id="二、安装-Visual-Studio"><a href="#二、安装-Visual-Studio" class="headerlink" title="二、安装 Visual Studio"></a>二、安装 Visual Studio</h2><p>接下来安装 Visual Studio，以 2017 社区版为例。</p><p>第一步，从微软官网下载 Visual Studio Installer，地址为：<a href="/link/?t=XBCRCE" target="_blank">https://visualstudio.microsoft.com/zh-hans/downloads/</a> ，点击下方所有下载中的 Visual Studio 2017 开始下载。</p><p>第二步，运行安装程序，安装 Visual Studio Installer。安装完成后，从开始菜单中打开 Installer，在 Visual Studio Community 2017 下方点击安装按钮开始安装，进入“工作负载”页面。</p><p><img src="/img/unity-vs-installer.png" alt="Visual Studio Installer 工作负载界面"></p><p>如上图，勾选“使用 Unity 的游戏开发”即可，然后进行安装。Installer 会下载 Visual Studio 并安装在指定位置。</p><p>待安装好之后，我们打开 Unity Editor -&gt; 编辑 -&gt; Preferences -&gt; 外部工具，可以查看到“外部脚本编辑器”这一项的值是 Visual Studio 2017 (Community)。此时双击 Editor 中的脚本文件，系统将会启动 Visual Studio 作为 IDE 进行编辑。</p><h2 id="三、使用-Visual-Studio-调试"><a href="#三、使用-Visual-Studio-调试" class="headerlink" title="三、使用 Visual Studio 调试"></a>三、使用 Visual Studio 调试</h2><p>在安装完开发环境之后，需要提及的是 Unity + Visual Studio 的代码调试功能。Visual Studio 可以启动 Unity 游戏进程来进行代码调试。我们要做的是，在 Visual Studio 中编辑完毕代码，在需要设置断点的代码行左侧单击设置断点（会出现一个红点）。之后选择“附加到 Unity 并播放”按钮开始调试，Visual Studio 会启动游戏进程并运行到断点处停止，显示各种代码状态。</p><p><img src="/img/unity-visual-studio-debug.png" alt="使用 Visual Studio 开始调试"></p><p>点击调试按钮后，界面会跳回 Unity 并开始运行游戏进程，当运行到断点时，跳回 Visual Studio 显示调试界面。</p><p>到此，环境安装完成，可以开始开发了！</p><p>以上。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;&lt;img src=&quot;/img/unity-header.jpg&quot; alt=&quot;Unity 2019.1, Picture from Unity Blog&quot;&gt;&lt;/p&gt;
&lt;p&gt;Unity 是一款优秀的2D&amp;#x2F;3D游戏引擎，使用 Unity 开发的游戏大作层出不穷。如今，U</summary>
      
    
    
    
    <category term="Computer Science" scheme="https://maphical.cn/categories/Computer-Science/"/>
    
    <category term="Game Engines" scheme="https://maphical.cn/categories/Computer-Science/Game-Engines/"/>
    
    <category term="Unity" scheme="https://maphical.cn/categories/Computer-Science/Game-Engines/Unity/"/>
    
    
  </entry>
  
  <entry>
    <title>使用OKHTTP框架异步更新UI组件</title>
    <link href="https://maphical.cn/2019/04/update-ui-using-okhttp/"/>
    <id>https://maphical.cn/2019/04/update-ui-using-okhttp/</id>
    <published>2019-04-14T04:47:57.000Z</published>
    <updated>2025-10-12T14:54:52.367Z</updated>
    
    <content type="html"><![CDATA[<p>在做 Android 开发时，我们免不了使用 HTTP 请求，有一个好用的框架对于开发的帮助很大，OKHttp（<a href="/link/?t=A01YRC" target="_blank">Github 地址</a>）是 Square 公司实现的开源 HTTP 请求框架，基本能够满足 Android 开发中的各种网络应用场景。</p><p>但在使用时，我发现 OKHttp 所提供的异步请求接口在执行回调函数时，不能更新 UI 组件，原因是框架执行回调，并不是在 UI 线程上执行的。这一点问题在开发时造成了一些不便。为了解决这个问题，我使用框架提供的同步请求接口封装了一个自己实现的异步接口，使得回调函数能够在 UI 线程上执行，也能够处理网络请求中出现的一些异常。实现如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">import</span> android.app.Activity;<br><span class="hljs-keyword">import</span> android.content.Context;<br><span class="hljs-keyword">import</span> android.util.Log;<br><br><span class="hljs-keyword">import</span> org.json.JSONException;<br><br><span class="hljs-keyword">import</span> java.io.IOException;<br><span class="hljs-keyword">import</span> java.util.concurrent.TimeUnit;<br><br><span class="hljs-keyword">import</span> okhttp3.Call;<br><span class="hljs-keyword">import</span> okhttp3.MediaType;<br><span class="hljs-keyword">import</span> okhttp3.OkHttpClient;<br><span class="hljs-keyword">import</span> okhttp3.Request;<br><span class="hljs-keyword">import</span> okhttp3.RequestBody;<br><br><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 用于发起HTTP请求的协助类。</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">HttpRequestHelper</span> &#123;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 请求的地址。</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">private</span> String url;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 请求的消息体。</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">private</span> String body;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 请求的方法。</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">private</span> String method;<br><br>    <span class="hljs-keyword">private</span> Call call;<br>    <span class="hljs-keyword">private</span> Activity activity;<br><br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> Boolean[] ifSuccessfully = &#123;<span class="hljs-literal">true</span>&#125;;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 构造方法。</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> c Context</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> u URL</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> m Method</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> b Body</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-title function_">HttpRequestHelper</span><span class="hljs-params">(Context c, String u, String m, String b)</span> &#123;<br>        url = u;<br>        method = m;<br>        body = b;<br>        activity = (Activity) c;<br><br>        <span class="hljs-comment">// 设置超时时间为10秒</span><br>        <span class="hljs-type">OkHttpClient</span> <span class="hljs-variable">client</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">OkHttpClient</span>.Builder()<br>                .readTimeout(<span class="hljs-number">30</span>, TimeUnit.SECONDS)<br>                .writeTimeout(<span class="hljs-number">30</span>, TimeUnit.SECONDS)<br>                .build();<br>        Request request;<br>        <span class="hljs-keyword">if</span> (m.equals(<span class="hljs-string">&quot;get&quot;</span>)) &#123;<br>            request = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Request</span>.Builder()<br>                    .url(u)<br>                    .get()<br>                    .build();<br>        &#125; <span class="hljs-keyword">else</span> &#123;<br>            request = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Request</span>.Builder()<br>                    .url(u)<br>                    .post(<br>                        RequestBody.create(<br>                            MediaType.parse(<span class="hljs-string">&quot;application/json&quot;</span>),b<br>                        )<br>                    )<br>                    .build();<br>        &#125;<br>        call = client.newCall(request);<br>    &#125;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 用于处理网络请求结果的方法。</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> rh 处理接口。</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> invoker 调用者Class</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> info 用于显示的Debug信息。</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">handleResponse</span><span class="hljs-params">(ResponseHandler rh, Class invoker,</span><br><span class="hljs-params">                               String info)</span> &#123;<br>        <span class="hljs-type">Thread</span> <span class="hljs-variable">t</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Thread</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">Runnable</span>() &#123;<br>            <span class="hljs-meta">@Override</span><br>            <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">run</span><span class="hljs-params">()</span> &#123;<br>                <span class="hljs-type">String</span> <span class="hljs-variable">response</span> <span class="hljs-operator">=</span> <span class="hljs-string">&quot;&quot;</span>;<br>                <span class="hljs-keyword">try</span> &#123;<br>                    Log.i(invoker.getName(),<br>                            <span class="hljs-string">&quot;HTTP发送数据\&quot;&quot;</span><br>                                    + info + <span class="hljs-string">&quot;\&quot;(方法&quot;</span><br>                                    + method + <span class="hljs-string">&quot;)：&quot;</span> + body<br>                                    + <span class="hljs-string">&quot; 到 &quot;</span> + url);<br>                    response = call.execute().body().string();<br>                    Log.i(invoker.getName(),<br>                            <span class="hljs-string">&quot;HTTP接收数据\&quot;&quot;</span><br>                                    + info + <span class="hljs-string">&quot;\&quot;：&quot;</span><br>                                    + response);<br>                &#125; <span class="hljs-keyword">catch</span> (IOException e) &#123;<br>                    Log.e(invoker.getName(),<br>                            <span class="hljs-string">&quot;HTTP获取异常\&quot;&quot;</span><br>                                    + info + <span class="hljs-string">&quot;\&quot;：&quot;</span><br>                                    + e.getMessage());<br>                    ifSuccessfully[<span class="hljs-number">0</span>] = <span class="hljs-literal">false</span>;<br>                &#125; <span class="hljs-keyword">finally</span> &#123;<br>                    <span class="hljs-type">String</span> <span class="hljs-variable">finalResponse</span> <span class="hljs-operator">=</span> response;<br>                    activity.runOnUiThread(<span class="hljs-keyword">new</span> <span class="hljs-title class_">Runnable</span>() &#123;<br>                        <span class="hljs-meta">@Override</span><br>                        <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">run</span><span class="hljs-params">()</span> &#123;<br>                            <span class="hljs-keyword">try</span> &#123;<br>                                rh.handle(finalResponse);<br>                            &#125; <span class="hljs-keyword">catch</span> (JSONException e) &#123;<br>                                Log.e(invoker.getName(),<br>                                        <span class="hljs-string">&quot;HTTP获取JSON解析异常\&quot;&quot;</span><br>                                                + info + <span class="hljs-string">&quot;\&quot;：&quot;</span><br>                                                + e.getMessage());<br>                                ifSuccessfully[<span class="hljs-number">0</span>] = <span class="hljs-literal">false</span>;<br>                            &#125; <span class="hljs-keyword">finally</span> &#123;<br>                                rh.handleResult();<br>                            &#125;<br>                        &#125;<br>                    &#125;);<br>                &#125;<br>            &#125;<br>        &#125;);<br>        t.start();<br>    &#125;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 返回网络请求是否成功。</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span> 是否成功。</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> Boolean <span class="hljs-title function_">ifSuccess</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">return</span> ifSuccessfully[<span class="hljs-number">0</span>];<br>    &#125;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 处理结果的接口。</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">ResponseHandler</span> &#123;<br>        <span class="hljs-comment">/**</span><br><span class="hljs-comment">         * 用于处理网络返回结果的回调方法。</span><br><span class="hljs-comment">         * <span class="hljs-doctag">@param</span> response 网络返回的字符串结果。</span><br><span class="hljs-comment">         * <span class="hljs-doctag">@throws</span> JSONException 可能的JSON异常。</span><br><span class="hljs-comment">         */</span><br>        <span class="hljs-keyword">void</span> <span class="hljs-title function_">handle</span><span class="hljs-params">(String response)</span> <span class="hljs-keyword">throws</span> JSONException;<br><br>        <span class="hljs-comment">/**</span><br><span class="hljs-comment">         * 用于判断请求是否成功，并对结果进行处理的回调方法。</span><br><span class="hljs-comment">         */</span><br>        <span class="hljs-keyword">void</span> <span class="hljs-title function_">handleResult</span><span class="hljs-params">()</span>;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>这个 HTTP 协助类用于发起异步 HTTP 请求，并对结果调用传入的回调函数进行处理。回调函数一共有两个，即 ResponseHandler 接口中的两个方法。</p><p>handle 方法用于处理网络返回的数据（已转换为字符串，代码中默认返回的和发送的数据都为 JSON 格式），在此方法中可以进行对数据的解析提取等操作。</p><p>handleResult 方法用于处理整个网络请求的结果，例如请求成功和请求失败（需要调用协助类中的 ifSuccess 方法进行判断）。对不同的结果进行不同的处理，对于 UI 组件的更新也是在此方法中进行的，此方法会在 UI 线程调用。注意，此方法不抛出任何异常，需要在方法中对异常进行处理。</p><p>协助类中的 ifSuccess 返回的结果包含了请求中任意异常的发生，也就是说，无论是 handle 方法中的异常，还是在请求中出现的异常，都能在 ifSuccess 方法返回的结果中体现，可以认为只要发生了异常，ifSuccess 方法返回的就是 false，否则为 true。</p><p>下面是调用协助类的代码示例：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-type">HttpRequestHelper</span> <span class="hljs-variable">hrh</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">HttpRequestHelper</span>(<br>    <span class="hljs-built_in">this</span>,<br>    authUrl,<br>    <span class="hljs-string">&quot;post&quot;</span>,<br>    loginData.toString()<br>);<br>hrh.handleResponse(<span class="hljs-keyword">new</span> <span class="hljs-title class_">HttpRequestHelper</span>.ResponseHandler() &#123;<br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">handle</span><span class="hljs-params">(String response)</span> <span class="hljs-keyword">throws</span> JSONException &#123;<br>        <span class="hljs-comment">// 解析自动登录JSON数据</span><br>        authData = <span class="hljs-keyword">new</span> <span class="hljs-title class_">JSONObject</span>(response);<br>        System.out.println(<span class="hljs-string">&quot;Auth接受数据：&quot;</span>+ authData.toString());<br>        <span class="hljs-keyword">if</span> (!authData.getString(<span class="hljs-string">&quot;username&quot;</span>).equals(username)) &#123;<br>            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">JSONException</span>(<span class="hljs-string">&quot;自动登录异常：用户名不对应&quot;</span>);<br>        &#125;<br>        ifLoginSuccessfully=<br>            Boolean.valueOf(authData.getString(<span class="hljs-string">&quot;auth_result&quot;</span>));<br>        <span class="hljs-keyword">if</span> (ifStudent) &#123;<br>            ifUploadStandardPhoto =<br>                authData.getBoolean(<span class="hljs-string">&quot;if_upload_standard_photo&quot;</span>);<br>         &#125;<br>    &#125;<br><br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">handleResult</span><span class="hljs-params">()</span> &#123;<br>        handleNetForAutoLogin(hrh, navigationView, ifLoginSuccessfully);<br>    &#125;<br>&#125;, <span class="hljs-built_in">this</span>.getClass(), <span class="hljs-string">&quot;自动登录&quot;</span>);<br></code></pre></td></tr></table></figure><p>调用的代码中，首先是实例化了一个 HttpRequestHelper hrh，确定了 URL、请求方法（“post”）和消息体数据（JSON对象字符串）。然后调用 hrh 对象的handleResponse 方法来启动请求，同时实例化回调方法的接口，将回调函数传入 handleResponse 方法。</p><p>可以看到，在 ResponseHandler 接口中，handle方法主要用来解析和提取网络返回的数据。在其他地方，我定义了一个 handleNetForAutoLogin 方法，用于对返回的网络数据的后续使用和处理。这里我把 handle 方法中解析出的数据（ifLoginSuccessfully）传入了 handleNetForAutoLogin 方法中进行处理，同时也传入了 hrh 和 一个 View 组件。传入 hrh 是为了检查网络请求是否出现了异常，若出现了异常，就不会使用解析出的数据，并通知用户发生了错误。若没有发生异常，则使用传入的数据进行下一步的操作。</p><p>需要注意的是，handleResult 方法在 HttpRequestHelper 类中是在 finally 块中执行的，也就是说，无论网络请求是否发生异常，handleResult 方法都一定会执行（所以就可以在此方法中判断网络请求是否发生异常）。</p><p>无论是 handle 还是 handleResult，在协助类中，他们都是被放在 activity.runOnUiThread 方法中执行，都可以直接操作和更新 UI 组件。</p><p>以上。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;在做 Android 开发时，我们免不了使用 HTTP 请求，有一个好用的框架对于开发的帮助很大，OKHttp（&lt;a href=&quot;/link/?t=A01YRC&quot; target=&quot;_blank&quot;&gt;Github 地址&lt;/a&gt;）是 Square 公司实现的开源 HTTP 请求框</summary>
      
    
    
    
    <category term="Computer Science" scheme="https://maphical.cn/categories/Computer-Science/"/>
    
    <category term="Platforms" scheme="https://maphical.cn/categories/Computer-Science/Platforms/"/>
    
    <category term="Android" scheme="https://maphical.cn/categories/Computer-Science/Platforms/Android/"/>
    
    
  </entry>
  
  <entry>
    <title>SCRAPY学习笔记（思维导图）</title>
    <link href="https://maphical.cn/2019/03/scrapy-study-note/"/>
    <id>https://maphical.cn/2019/03/scrapy-study-note/</id>
    <published>2019-03-04T04:27:25.000Z</published>
    <updated>2025-10-12T14:54:52.367Z</updated>
    
    <content type="html"><![CDATA[<p>在学校进行科创项目时，由于需要用到 Scrapy 爬虫框架，花了一段时间自学，期间整理的思维导图记录在此。</p><h3 id="一、关于-Scrapy"><a href="#一、关于-Scrapy" class="headerlink" title="一、关于 Scrapy"></a>一、关于 Scrapy</h3><p>Scrapy 是一个 Python 爬虫框架，可以很方便地使用此框架编写爬虫并爬取解析网络数据，Scrapy 官网在<a href="/link/?t=UWLQ4R" target="_blank">这里</a>。</p><p>安装 Scrapy：</p><p><strong>1.使用 Anaconda 进行安装</strong></p><p>在 Windows 平台安装 Scrapy 需要使用 Anaconda Distribution，因为如果直接使用自己安装的 Python PIP 包管理器进行安装可能会导致依赖组件的编译问题（可能需要特定的C++运行库），使用 Anaconda 进行安装的步骤：</p><ul><li>安装 Anaconda Distribution；</li><li>打开 Anaconda Prompt；</li><li>执行命令 conda install -c conda-forge scrapy；</li><li>等待安装完成即可。</li></ul><p><strong>2.使用 pip 包管理器进行安装</strong></p><p>使用虚拟环境或者系统 Python 命令行安装 scrapy。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">pip install scrapy<br></code></pre></td></tr></table></figure><p>或</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">pip3 install scrapy<br></code></pre></td></tr></table></figure><h3 id="二、Scrapy-常用命令"><a href="#二、Scrapy-常用命令" class="headerlink" title="二、Scrapy 常用命令"></a>二、Scrapy 常用命令</h3><p><strong>startproject</strong></p><p>在 Anaconda Prompt 中执行</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">scrapy startproject &lt;PROJECT_NAME&gt;<br></code></pre></td></tr></table></figure><p>**用途：**创建项目（Project），即创建一个项目文件夹并生成项目中的文件架构。</p><p><strong>crawl</strong></p><p>在 Anaconda Prompt 中执行</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">cd</span> &lt;PROJECT_DIR&gt;<br>scrapy crawl &lt;SPIDER_NAME&gt;<br></code></pre></td></tr></table></figure><p>**用途：**启动一个编写好的爬虫，爬虫类中 name 属性的值为爬虫的名称（即以上<SPIDER_NAME>）。</p><p><strong>shell</strong></p><p>在 Anaconda Prompt 中执行</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">scrapy shell <span class="hljs-string">&quot;&lt;TARGET_URL&gt;&quot;</span><br></code></pre></td></tr></table></figure><p>**用途：**以交互模式启动，抓取 URL 中的网页后提供交互式命令行供用户手动分析，一般用于设计爬虫时的网页元素分析。</p><h3 id="三、Scrapy-部分组件思维导图"><a href="#三、Scrapy-部分组件思维导图" class="headerlink" title="三、Scrapy 部分组件思维导图"></a>三、Scrapy 部分组件思维导图</h3><p><strong>Scrapy Spider</strong></p><p><img src="/img/scrapy-scrapy-spider.png" alt="Scrapy Spider部分属性和方法"></p><p><strong>Scrapy Item</strong></p><p><img src="/img/scrapy-scrapy-item.png" alt="Scrapy Item相关"></p><p><strong>Scrapy ItemLoader</strong></p><p><img src="/img/scrapy-scrapy-itemloader.png" alt="Scrapy ItemLoader相关"></p><p><strong>Scrapy ItemPipeline</strong></p><p><img src="/img/scrapy-scrapy-itempipeline.png" alt="Scrapy ItemPipeline相关"></p><p><strong>Scrapy Request</strong></p><p><img src="/img/scrapy-scrapy-request.png" alt="Scrapy Request相关"></p><p><strong>Scrapy AutoThrottle Extension</strong></p><p><img src="/img/scrapy-scrapy-autothrottle-extension.png" alt="Scrapy AutoThrottle Extension相关"></p><p>以上。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;在学校进行科创项目时，由于需要用到 Scrapy 爬虫框架，花了一段时间自学，期间整理的思维导图记录在此。&lt;/p&gt;
&lt;h3 id=&quot;一、关于-Scrapy&quot;&gt;&lt;a href=&quot;#一、关于-Scrapy&quot; class=&quot;headerlink&quot; title=&quot;一、关于 Scra</summary>
      
    
    
    
    <category term="Computer Science" scheme="https://maphical.cn/categories/Computer-Science/"/>
    
    <category term="Frameworks &amp; Tools" scheme="https://maphical.cn/categories/Computer-Science/Frameworks-Tools/"/>
    
    <category term="Scrapy" scheme="https://maphical.cn/categories/Computer-Science/Frameworks-Tools/Scrapy/"/>
    
    
  </entry>
  
  <entry>
    <title>使用JAVA搭建小程序WEBSOCKET服务器</title>
    <link href="https://maphical.cn/2018/11/setup-websocket-server-using-java/"/>
    <id>https://maphical.cn/2018/11/setup-websocket-server-using-java/</id>
    <published>2018-11-24T08:36:35.000Z</published>
    <updated>2025-10-12T14:54:46.495Z</updated>
    
    <content type="html"><![CDATA[<p>最近参加了一些计算机比赛，我们队根据题目要求制作了一个使用微信小程序作为客户端的实时社交应用系统，使用 Java 开发服务器端。因为小程序只支持 Https 和 WebSocket 方式与服务器交互，同时题目要求小程序具有实时通讯的功能（例如实时消息），所以我们使用 WebSocket 实现实时交互。</p><p>我们都知道，目前的网络应用大部分是客户端&#x2F;服务器（Client&#x2F;Server，C&#x2F;S）架构，客户端可以使用 HTTP 协议去获取服务器端的内容，使用 HTTP 协议一定是客户端主动地去向服务器发出请求，服务器根据客户端的请求来返回相应的内容。传统的网站以及对信息实时性要求不高的网络应用使用 HTTP 协议获取内容是很好的选择，但是对于具有实时通讯功能（用户彼此实时交流）的应用来说，更好的选择是 WebSocket 协议。WebSocket 协议可以使客户端与服务器保持连接，并打破了只能是客户端主动获取服务器内容的局限，服务器可以主动向客户端推送信息，形成双向的信息流。</p><p>在 Java 平台使用 WebSocket 也是非常方便的，Java Specification Requests（JSR）标准中的 JSR356（<a href="/link/?t=O68M3L" target="_blank">https://jcp.org/en/jsr/detail?id=356</a>）叙述了 WebSocket 的 Java API 使用方式，遵循这套模型开发的 WebSocket 代码，可以在不同的应用容器中运行（例如 Tomcat），应用容器 WebSocket 部分的实现都遵循这套标准。</p><p>对 JSR356 的简介如下：</p><blockquote><p>JSR 356, Java API for WebSocket, specifies the API that Java developers can use when they want to integrate WebSockets into their applications—both on the server side as well as on the Java client side. Every implementation of the WebSocket protocol that claims to be compliant with JSR 356 must implement this API. As a consequence, developers can write their WebSocket-based applications independent of the underlying WebSocket implementation. This is a huge benefit, because it prevents a vendor-lock and allows for more choices and freedom of libraries and application servers.</p><p>2018年11月22日摘自 Oracle，<em>JSR 356, Java API for WebSocket</em>，<em>by Johan Vos</em>，<a href="/link/?t=U68DNZ" target="_blank">https://www.oracle.com/technetwork/articles/java/jsr356-1937161.html</a></p></blockquote><h3 id="一、编写-WebSocket-服务器"><a href="#一、编写-WebSocket-服务器" class="headerlink" title="一、编写 WebSocket 服务器"></a>一、编写 WebSocket 服务器</h3><p>使用 Java 编写 WebSocket 服务器端不难，我们可以使用上述提到的 Java WebSocket API 来实现，这篇文章中的代码运行于 Tomcat。要让 Tomcat 识别代码中的 WebSocket Endpoint 类和各个用于处理事件的方法，我们需要用到注解。一个大概的 WebSocket Endpoint 类的结构是这样的（需要导入 javax.websocket.* 和 javax.websocket.server.ServerEndpoint）：</p><ul><li>@ServerEndpoint(value&#x3D;”&#x2F;path&#x2F;to&#x2F;endpoint”)注解（类）</li><li>@OnOpen注解（方法）</li><li>@OnMessage注解（方法）</li><li>@OnClose注解（方法）</li><li>@OnError注解（方法）</li></ul><p><strong>@ServerEndpoint(value&#x3D;”&#x2F;path&#x2F;to&#x2F;endpoint”)</strong></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><code class="hljs java"><span class="hljs-meta">@ServerEndpoint(value=&quot;/path/to/endpoint&quot;)</span><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">WebsocketEndpoint</span> &#123;...&#125;<br></code></pre></td></tr></table></figure><p>此注解用于标记实现 WebSocket 端点的类，即用于处理 WebSocket 消息、WebSocket 连接、WebSocket 异常的类。被此注解标记的类，会被 Tomcat 用于处理由客户端发起的 WebSocket 连接，具体对于连接进行处理的方式被定义在此类的各个方法中。</p><p>注解中的 value 属性的值定义了 WebSocket 端点在服务器上的访问路径，例如若服务器的域名为 maphical.cn，且 value 的值为“&#x2F;path&#x2F;to&#x2F;endpoint”，则访问 WebSocket 端点的链接为“wss:&#x2F;&#x2F;maphical.cn&#x2F;path&#x2F;to&#x2F;endpoint”。</p><p><strong>@OnOpen</strong></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><code class="hljs java"><span class="hljs-meta">@OnOpen</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">onOpen</span><span class="hljs-params">(javax.websocket.Session sess)</span><br></code></pre></td></tr></table></figure><p>此注解标记用于处理新 WebSocket 连接的方法，当 Tomcat 接收到新的连接请求并建立连接后，将会调用此方法。此注解标记的方法在 WebSocket 服务中一般被用来将新会话保存进类中维护的会话列表中，使得系统能够在后续发送消息是能够根据 Session ID 找到相应的会话对象。同时也可以在此方法中维护 WebSocket 客户端连接数量等附加信息。</p><p><strong>@OnMessage</strong></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><code class="hljs java"><span class="hljs-meta">@OnMessage</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">onMessage</span><span class="hljs-params">(String mess, javax.websocket.Session sess)</span><br></code></pre></td></tr></table></figure><p>此注解标记的方法用于处理 WebSocket 会话发送到服务器的消息，消息内容将会被以字符串参数的形式传入方法（上述代码中的 mess 参数）。当 Tomcat 接收到一条来自某个会话的消息后，会调用此方法并将消息内容与发送消息的会话对象作为参数一同传入方法中，之后的处理是由程序员的代码来完成。对于实时通讯类应用服务器来说，这个方法的通常用途是作为消息中转站，将接收到的消息进行处理转发到目标客户端。</p><p><strong>@OnClose</strong></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><code class="hljs java"><span class="hljs-meta">@OnClose</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">onClose</span><span class="hljs-params">(javax.websocket.Session sess)</span><br></code></pre></td></tr></table></figure><p>此注解标记的方法用于处理 WebSocket 连接的关闭，当 Tomcat 关闭或者客户端即将关闭某个链接时，将会调用此方法，并将即将被关闭的会话对象作为参数传入（上述代码中的 sess 参数）。此方法的一般用途为，进行连接被关闭时的清理工作，例如从会话列表中删除即将关闭的会话等。</p><p><strong>@OnError</strong></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><code class="hljs java"><span class="hljs-meta">@OnError</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">onError</span><span class="hljs-params">(javax.websocket.Session sess, Throwable e)</span><br></code></pre></td></tr></table></figure><p>此注解标记的方法用于处理其它过程中产生的异常，当发生异常时 Tomcat 会调用此方法，并将发生异常的会话对象以及异常对象作为参数传入方法中（上述代码中的 sess 和 e 参数）。此方法的一般用途为，通知客户端服务器发生异常，并为异常进行清理工作和日志的记录等。</p><h3 id="二、部署-WebSocket-服务器"><a href="#二、部署-WebSocket-服务器" class="headerlink" title="二、部署 WebSocket 服务器"></a>二、部署 WebSocket 服务器</h3><p>将端点类编写完毕之后，不需要改动其他的配置文件等，直接将项目打包成 war 文件并上传到 Tomcat 即可自动部署，服务器会自动识别代码中的注解，部署完毕后即可使用 WebSocket 客户端（如小程序等）连接 WebSocket 端点进行测试。</p><p>需要说明的是，小程序不支持没有进行 SSL 加密的 WebSocket 连接，需要提前在服务器上部署并配置域名 SSL 证书，并使用 wss:&#x2F;&#x2F; 进行访问（wss 意为经过 SSL 加密的 WebSocket 协议，ws 意为没有经过 SSL 加密的 WebSocket 协议）。</p><p>WebSocket 的连接建立使用 HTTP 头，在建立 TCP 连接之后，服务器根据 HTTP 头部中的信息切换到 WebSocket 协议，所以 ws 默认使用 80 端口，wss 默认使用 443 端口。若你的服务器已经为 HTTPS 配置好了 SSL 证书和端口，那就可以直接使用 wss 协议访问 WebSocket 端点。</p><p>以上。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;最近参加了一些计算机比赛，我们队根据题目要求制作了一个使用微信小程序作为客户端的实时社交应用系统，使用 Java 开发服务器端。因为小程序只支持 Https 和 WebSocket 方式与服务器交互，同时题目要求小程序具有实时通讯的功能（例如实时消息），所以我们使用 Web</summary>
      
    
    
    
    <category term="Computer Science" scheme="https://maphical.cn/categories/Computer-Science/"/>
    
    <category term="Programming Language" scheme="https://maphical.cn/categories/Computer-Science/Programming-Language/"/>
    
    <category term="Java" scheme="https://maphical.cn/categories/Computer-Science/Programming-Language/Java/"/>
    
    
  </entry>
  
  <entry>
    <title>文明的旅程——《三体》读后感</title>
    <link href="https://maphical.cn/2018/06/three-body-reading-note/"/>
    <id>https://maphical.cn/2018/06/three-body-reading-note/</id>
    <published>2018-06-16T09:45:44.000Z</published>
    <updated>2025-10-11T16:10:29.448Z</updated>
    
    <content type="html"><![CDATA[<p>今天我把从很久之前就开始读第一部的三体全册都读完了，因为各种事情忘记了读，以至于读这套书一共经历了三年的时间，以至于我每次要开始看的时候都要努力地回想之前的情节，甚至要去翻翻书才能想得起来。虽然如此，我还是每次都能回忆得起来之前的情节，也许是因为这本小说我对它的印象比较深吧。</p><p>其实，看一本书，或者一套小说，应该是要连续的读完的。这样才能将前后的情节连贯起来，更加准确地了解各个人物的特点和小说的主旨大意，也更能获得情感上的共鸣。但也正因为我断断续续地读了两年，每次都要回忆，所以我对这本小说的印象应该会比较深刻了。</p><p>整套小说描写了地球文明从现代开始，一直到宇宙即将坍缩的末日，时间跨度达到上百亿年，主题非常宏大，构思也是非常的精妙。这是一部硬科幻，小说中所描写的所有，似乎都和我们现在所生活的世界息息相关，都似乎是从现实世界衍生的，完全有可能发生的事件。同时也不得不叹服作者的知识渊博，身为工程师的刘慈欣，从计算机和物理学，到政治和美学艺术，都在作品中展示了他对这些领域的了解。在我看来，如果没有这些深入的积累，这部作品是写不出来的，十分佩服。</p><p>我从小就对天文和科幻十分感兴趣，小时候也是无数次“模拟”太空基地和飞船等太空科幻元素，也有过无数次的对星空的幻想。在《三体》中，我几乎享受了我所期望它有的所有科幻元素，不仅如此，这些科幻元素都基于现实的世界，这让这部作品看起来似乎是一部史书，是以几个地球生命的视角来记录整个宇宙的发展史，向读者一步一步地揭露了宇宙的险恶面目和文明发展的艰难。</p><p>但它又不仅仅是一部生硬的科幻，作者又将主人公的感情心理和成长历程，地球文明对道路的选择以及对道德的重新认识等等触人心弦的元素融入了其中。让我看完之后回味无穷，这其中既有对时间的无情的感慨，又有对太阳系人类道路选择的惋惜。这部作品似乎从哲学的高度重新让读者认识了一遍宇宙。我们生命的意义是什么？时间是无穷无尽的吗？文明的高度能有多高？在生存和道德面前我们应该如何选择？</p><p>可以看到，太阳系人类的文明无论如何挣扎，都逃脱不了毁灭的命运，这也是大多数人的命运，真正能够摆脱这种命运的人，都要先摆脱人类的道德约束，先认清现实的残酷。这里有一个争议很大的人物——程心。有很多人对她的评价不是很好，认为她在担负人类命运的时候总是会做出错误的决定，“短视”、“自私”、“妇人之仁”等不好的词语都被加在了她的身上。而我认为，这责任远远轮不到她来背负，就如“执剑人”那次转折说来，那种选择并不是程心做出的，而是所有赞成程心担任执剑人的选民所做出的选择，也就是，那是全体人类的选择。在我看来，即使没有程心这个人物的出现，地球人类也会选出第二个程心，这种结果并不是程心的错，而是人类的错误。是当时人类幼稚、天真的想法和社会文化所导致的必然产物，说白了，也就是人类种族的特性所致，这怪不了任何人，但与任何人都有关系。如果说程心有什么错的话，那就是她不能知错而改，在维德履行承诺的时候，她不能吸取教训相信维德一次，而是继续相信自己的判断。</p><p>有时我会想，如果维德能够代替程心的位置该多好，或者程心能够拥有像维德一样的果断和正确的思维判断，那样他或她都会引导太阳系文明走上正确的道路。</p><p>但是这是不可能的，如果程心像维德一样，在一开始云天明也不一定会喜欢上她，也就不一定能够送给她星星，她也就不一定能够获得启动星环集团资金，后来也就不一定能拥有研发光速飞船的资源了。</p><p>总之，即使太阳系文明能够一直发展下去，最终也是要面临毁灭的结局，到了时间的尽头，只有死神能够永生。</p><p>以上。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;今天我把从很久之前就开始读第一部的三体全册都读完了，因为各种事情忘记了读，以至于读这套书一共经历了三年的时间，以至于我每次要开始看的时候都要努力地回想之前的情节，甚至要去翻翻书才能想得起来。虽然如此，我还是每次都能回忆得起来之前的情节，也许是因为这本小说我对它的印象比较深吧</summary>
      
    
    
    
    <category term="Reading Note" scheme="https://maphical.cn/categories/Reading-Note/"/>
    
    <category term="Novel" scheme="https://maphical.cn/categories/Reading-Note/Novel/"/>
    
    
  </entry>
  
  <entry>
    <title>部署LAMP应用程序（附环境安装脚本）</title>
    <link href="https://maphical.cn/2018/05/deploy-lamp-server/"/>
    <id>https://maphical.cn/2018/05/deploy-lamp-server/</id>
    <published>2018-05-05T07:32:21.000Z</published>
    <updated>2025-10-12T14:54:52.367Z</updated>
    
    <content type="html"><![CDATA[<p>以目前来看，基于微信公众平台和小程序的应用程序开发是很普遍的，需求量也是很大的。经过几次LAMP环境应用程序的部署工作后，我觉得有必要把一般在Linux平台部署基于LAMP环境的应用程序的一般步骤记录下来作为经验积累。</p><p>为了部署过程更加便捷，我将此文章中所述的环境搭建步骤写成了 Python 脚本，使用此脚本可以自动安装LAMPP（Apache、MySQL、PHP、PHPMyAdmin）环境，可以点击<a href="/link/?t=HELLOZ" target="_blank">这里</a>下载安装脚本。</p><h3 id="LAMP环境"><a href="#LAMP环境" class="headerlink" title="LAMP环境"></a>LAMP环境</h3><p>LAMP是Linux，Apache，MySQL，PHP的缩写。Linux为操作系统，Apache为web服务器另外还提供PHP模块，MySQL是数据库。MySQL一般会有两种版本的选择，一是MySQL5.6，二是比较新的版本。PHP也是这样，一般现在会选择PHP7.x。另外，一般我接触过的会使用LAMP环境作为运行环境的应用程序为——使用PHP CI框架开发的为微信公众号或小程序提供后端服务的应用程序。</p><h3 id="部署步骤"><a href="#部署步骤" class="headerlink" title="部署步骤"></a>部署步骤</h3><h4 id="一、获得Linux服务器"><a href="#一、获得Linux服务器" class="headerlink" title="一、获得Linux服务器"></a>一、获得Linux服务器</h4><p>当然了，应用程序需要一个机器运行，可以从云平台上或者其他途径购买或获得一台Linux系统的服务器（一般使用CentOS或Ubuntu系统）。</p><h4 id="二、安装所需软件（CentOS为例）"><a href="#二、安装所需软件（CentOS为例）" class="headerlink" title="二、安装所需软件（CentOS为例）"></a>二、安装所需软件（CentOS为例）</h4><p><strong>1.安装Apache服务器</strong></p><p>更新yum软件源：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">yum makecache<br></code></pre></td></tr></table></figure><p>安装Apache：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">yum install httpd<br></code></pre></td></tr></table></figure><p>安装完成后开启Apache：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">service httpd start<br></code></pre></td></tr></table></figure><p>设置开机启动：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">chkconfig httpd on<br></code></pre></td></tr></table></figure><p>可能需要的设置有：</p><ol><li>防火墙打开http和https端口。</li><li>selinux可能需要关闭或设置。</li></ol><p><strong>2.安装MySQL数据库</strong></p><p>MySQL数据库在CentOS上不在默认的软件源中，它被MySQL的开源分支MariaDB替换了。CentOS中自带了MariaDB的安装，所以需要先卸载。</p><p>卸载预装的MariaDB：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">yum remove mariadb-libs<br></code></pre></td></tr></table></figure><p>访问MySQL官网下载安装MySQL源：<a href="/link/?t=NIZT9E" target="_blank">https://dev.mysql.com/downloads/repo/yum/</a></p><p>CentOS为例，在上述页面中根据CentOS版本选择Redhat Enterprise Linux 6或7的rpm下载并安装。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">yum localinstall 包名称<br></code></pre></td></tr></table></figure><p>安装好之后使用下面的命令查看当前选择准备安装的MySQL版本：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">yum repolist all | grep mysql<br></code></pre></td></tr></table></figure><p>如果我们想安装的mysql版本右边显示的不是enable，就要切换安装版本：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">vim /etc/yum.repos.d/mysql-community.repo<br></code></pre></td></tr></table></figure><p>文件中的格式就像下面这样的，查看方括号中的mysql80，寻找到想安装的版本，并把enabled的值改成1即可启用，另外要将原本启用的版本enabled改成0.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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><code class="hljs bash">[mysql80-community] <br>name=MySQL 8.0 Community Server <br>baseurl=http://repo.mysql.com/yum/mysql-8.0-community/el/6/<span class="hljs-variable">$basearch</span>/ <br>enabled=1 <br>gpgcheck=1 <br>gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql<br></code></pre></td></tr></table></figure><p>然后可以使用下面的命令查看一下是否启用：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">yum repolist enabled | grep mysql<br></code></pre></td></tr></table></figure><p>若想要安装的版本已经启用，就直接使用下面的命令安装：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">yum install mysql-community-server<br></code></pre></td></tr></table></figure><p>安装完成后启动MySQL服务器：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">service mysqld start<br></code></pre></td></tr></table></figure><p>获得root的初始密码：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">grep <span class="hljs-string">&#x27;temporary password&#x27;</span> /var/log/mysqld.log<br></code></pre></td></tr></table></figure><p>使用命令登录MySQL并更改密码：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">mysql -uroot -p<br>ALTER USER <span class="hljs-string">&#x27;root&#x27;</span>@<span class="hljs-string">&#x27;localhost&#x27;</span> IDENTIFIED BY <span class="hljs-string">&#x27;password&#x27;</span>;<br></code></pre></td></tr></table></figure><p>使用下面的命令进行安全性设置（仅限于MySQL5.6）：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">mysql_secure_installation<br></code></pre></td></tr></table></figure><p>设置数据库开机启动：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">chkconfig mysqld on<br></code></pre></td></tr></table></figure><p>数据库安装完成。</p><p><strong>3.安装PHP7.x</strong></p><p>在CentOS上默认软件源目前是没有PHP7.x的，我们需要自己添加。参照下方网页的指示：<br><a href="/link/?t=SJCVPF" target="_blank">https://webtatic.com/packages/php72/</a><br>这是一个提供CentOS软件源的网站。</p><p>获得PHP7.x的软件源：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">yum install epel-release<br>rpm -Uvh https://mirror.webtatic.com/yum/el7/webtatic-release.rpm<br></code></pre></td></tr></table></figure><p>安装用于Apache的PHP模块：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">yum install mod_php72w php72w-opcache<br></code></pre></td></tr></table></figure><p>另外还要安装一些工具库：</p><p>数据库连接库<br>php72w-mysqlnd</p><p>mbstring库<br>php72w-mbstring</p><p>PHP安装完成。</p><h4 id="三、设置环境"><a href="#三、设置环境" class="headerlink" title="三、设置环境"></a>三、设置环境</h4><p>1.设置服务器的时间（PHP的时间）</p><p>修改php.ini文件：</p><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ini"><span class="hljs-attr">date.timezone</span>=Asia/Shanghai<br></code></pre></td></tr></table></figure><p>2.如果使用的是CI框架，就要开启Apache的url重写功能：</p><p>找到&#x2F;etc&#x2F;httpd&#x2F;conf&#x2F;httpd.conf将</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">LoadModule rewrite_module modules/mod_rewrite.so<br></code></pre></td></tr></table></figure><p>这一行的注释符号#去掉，并在下方的None改成All即可。</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></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">Directory</span> &quot;/<span class="hljs-attr">var</span>/<span class="hljs-attr">www</span>/<span class="hljs-attr">html</span>&quot;&gt;</span><br> AllowOverride None<br><span class="hljs-tag">&lt;/<span class="hljs-name">Directory</span>&gt;</span><br></code></pre></td></tr></table></figure><h4 id="四、完成部署"><a href="#四、完成部署" class="headerlink" title="四、完成部署"></a>四、完成部署</h4><p>现在已经基本完成部署，可以把程序代码上传到服务器上来测试一下了。</p><p>以上。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;以目前来看，基于微信公众平台和小程序的应用程序开发是很普遍的，需求量也是很大的。经过几次LAMP环境应用程序的部署工作后，我觉得有必要把一般在Linux平台部署基于LAMP环境的应用程序的一般步骤记录下来作为经验积累。&lt;/p&gt;
&lt;p&gt;为了部署过程更加便捷，我将此文章中所述的</summary>
      
    
    
    
    <category term="Computer Science" scheme="https://maphical.cn/categories/Computer-Science/"/>
    
    <category term="Operation &amp; Maintenance" scheme="https://maphical.cn/categories/Computer-Science/Operation-Maintenance/"/>
    
    
  </entry>
  
  <entry>
    <title>使用GITLAB的API自动更新部署代码</title>
    <link href="https://maphical.cn/2018/01/auto-update-repo-using-gitlab-api/"/>
    <id>https://maphical.cn/2018/01/auto-update-repo-using-gitlab-api/</id>
    <published>2018-01-13T14:18:13.000Z</published>
    <updated>2025-10-12T14:54:52.367Z</updated>
    
    <content type="html"><![CDATA[<p>对于一些小项目而言，在 Gitlab 上开发完成并部署到服务器上后，出于修改或者增加功能的需要，常常要更新服务器中的程序代码，我们当然可以手动将更新的代码上传到服务器中，但是这样无疑要增加很多繁琐的操作步骤。对于使用 Gitlab 托管代码的应用程序而言，我们完全可以使用 Gitlab 的 API 和服务器中的脚本来实现代码的自动更新。</p><p>实现自动更新的基本方法是，通过 Gitlab 项目的 Webhook 功能，在每次发生了可以触发 Hook 的事件后，Gitlab 会向一个我们填写的 URL 发送包含项目和事件信息的 JSON 数据。接收到数据后，服务器上的脚本就可以通过数据判断需要更新哪些文件，再删除或者通过 Gitlab 的 API 来更新这些文件即可。</p><p>下面来记录一下如何操作。</p><h3 id="一、为项目设置-Webhook"><a href="#一、为项目设置-Webhook" class="headerlink" title="一、为项目设置 Webhook"></a>一、为项目设置 Webhook</h3><p>首先登陆 Gitlab 并进入项目的 Settings -&gt; Integrations 选项中，在 URL 一栏中填写想要发送数据的 URL，下面的 Secret Token 用于验证 Gitlab 的身份，填写的字符会被在名为 X-Gitlab-Token 的 HTTP Header 中和数据一起被发送，这样服务器就能确认这是从 Gitlab 发送的数据而不是其它假冒的来源。</p><p><img src="/img/gitlab-webhook-url.png" alt="Webhook URL 和 Secret Token"></p><p>在这一栏的下方，有一些可以选择的能够触发 Webhook 的项目事件，关于这些事件的信息都会被一起发送到指定的 URL 中。设置好了之后就可以在服务器上写一个测试脚本来测试能否收到 Webhook 的数据，Gitlab 会在 Webhook 的 Events 页面中显示服务器返回的数据，可以以此来检测工作状态。</p><h3 id="二、解析-Webhook-发送的数据"><a href="#二、解析-Webhook-发送的数据" class="headerlink" title="二、解析 Webhook 发送的数据"></a>二、解析 Webhook 发送的数据</h3><p>关于 Webhook 发送的数据格式的帮助可以在它的<a href="/link/?t=XQL3YJ" target="_blank">官网</a>上找到，更新项目的代码至少需要获取的数据有（括号里为将 JSON 数据转换为 PHP 对象后的结构，JSON 数据对象用“data”代替）：</p><ul><li>项目的 ID（data-&gt;project_id）</li><li>项目中被更改的文件路径（data-&gt;commits[0]-&gt;added&#x2F;modified&#x2F;removed[0]）</li></ul><p>在将 JSON 转换为 PHP 对象后，其中 commits[0] 是指 commits 数组里的第一个 commit，这个 commit 是一个对象，其中的 added、modified 和 removed 是三个数组，里面分别存放着被添加的、被修改的和被移除的文件在项目中的路径。</p><h3 id="三、使用-Gitlab-的-API-进行代码的更新"><a href="#三、使用-Gitlab-的-API-进行代码的更新" class="headerlink" title="三、使用 Gitlab 的 API 进行代码的更新"></a>三、使用 Gitlab 的 API 进行代码的更新</h3><p>有了以上的数据，我们就能够知道要从 Gitlab 获取什么内容了，我的操作是将添加的（added）和修改的（modified）都下载到服务器上并覆盖原来的文件（无论原来文件是否存在），将移除的（removed）文件从服务器上删掉。</p><p>然而，要从 Gitlab 获取更新后的代码，只有这些文件的路径和项目 ID 是不够的。还需要拥有权限的 Gitlab 用户的 Personal Access Token 来调用 API，接下来我们就设置 Token。</p><p>打开对项目有查看权限的用户的 Settings 页面，选择 Access Tokens 选项，进入这个页，会看到 Personal Access Token 一栏。</p><p><img src="/img/gitlab-personal-access-token.png" alt="Persional Access Token 页面"></p><p>在 Name 一栏中填写这个 Token 的标识名字（主要是为了帮自己查看），可以在下面的 Expires at 一栏中设置有效时长，也可以不填即为永久有效。在下面有几个选项，这是 Token 可以被授权的功能，要更新代码只需要获取文件内容的 API 即可，所以我们可以选择只选择 api。</p><p>填写好后，点击 Create personal access token 按钮创建 Token 即可。然后将显示的 Token 记下，它将不被 Gitlab 保存记录，也就是说 Token 只显示这一次，以后无法查看，所以要将它保管好。</p><p>Token 要被放在 API 请求的 HTTP Header 中，名为 PRIVATE-TOKEN，这样才能被 Gitlab 识别。</p><h3 id="四、使用-API-自动更新代码"><a href="#四、使用-API-自动更新代码" class="headerlink" title="四、使用 API 自动更新代码"></a>四、使用 API 自动更新代码</h3><p>有了 Token，Project ID 和被更改文件的项目路径，我们就可以实现自动更新代码的功能了。</p><p>我们可以在 Webhook 指向的 URL 处写一个脚本来实现这一功能：</p><ol><li>获取 Webhook 发送的信息；</li><li>使用 Personal access token 通过 Gitlab 的 API 获取更新文件的内容；</li><li>更新或删除服务器上的代码。</li></ol><p>以 PHP 为例，我们可以使用 curl 函数来调用 API，需要注意的是，Gitlab 的 API 调用 URL 中，要获取的项目中文件的路径需要使用 URL 转码过的字符串，这些使用说明可以在<a href="/link/?t=UF2I4H" target="_blank">这里</a>找到。</p><p>以上。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;对于一些小项目而言，在 Gitlab 上开发完成并部署到服务器上后，出于修改或者增加功能的需要，常常要更新服务器中的程序代码，我们当然可以手动将更新的代码上传到服务器中，但是这样无疑要增加很多繁琐的操作步骤。对于使用 Gitlab 托管代码的应用程序而言，我们完全可以使用 </summary>
      
    
    
    
    <category term="Computer Science" scheme="https://maphical.cn/categories/Computer-Science/"/>
    
    <category term="Operation &amp; Maintenance" scheme="https://maphical.cn/categories/Computer-Science/Operation-Maintenance/"/>
    
    
  </entry>
  
  <entry>
    <title>使用STYLE-TRANSFER转移照片艺术风格</title>
    <link href="https://maphical.cn/2018/01/transfer-artistic-style-using-style-transfer/"/>
    <id>https://maphical.cn/2018/01/transfer-artistic-style-using-style-transfer/</id>
    <published>2018-01-04T09:17:09.000Z</published>
    <updated>2025-10-12T14:54:46.495Z</updated>
    
    <content type="html"><![CDATA[<p>最近几天学习了一下如何使用 Github 上的项目“style-transfer”（<a href="/link/?t=NSC9B0" target="_blank">https://github.com/fzliu/style-transfer</a>）来转移照片的艺术风格。用这篇文章记录一下操作过程，即如何配置运行环境、如何使用“style-transfer”完成操作。</p><h3 id="一、style-transfer"><a href="#一、style-transfer" class="headerlink" title="一、style-transfer"></a>一、style-transfer</h3><p>Github 中有一个照片风格算法的 Python 实现，就是上述的这个项目，这个算法发表在“A Neural Algorithm of Artistic Style”（<a href="/link/?t=H79NXX" target="_blank">http://arxiv.org/abs/1508.06576</a>）中。先贴一下项目的 README 的 Introduction 部分：</p><blockquote><p>Introduction This repository contains a pyCaffe-based implementation of “A Neural Algorithm of Artistic Style” by L. Gatys, A. Ecker, and M. Bethge, which presents a method for transferring the artistic style of one input image onto another. You can read the paper here: <a href="/link/?t=H79NXX" target="_blank">http://arxiv.org/abs/1508.06576</a>. Neural net operations are handled by Caffe, while loss minimization and other miscellaneous matrix operations are performed using numpy and scipy. L-BFGS is used for minimization.</p><p>style-transfer by fzliu, <a href="/link/?t=NSC9B0" target="_blank">https://github.com/fzliu/style-transfer</a></p></blockquote><p>这个程序可以将一张照片的艺术风格转移到另一张照片中，我要做的也就是经典的“油画风格转移”，即将梵高的《星夜》作为输入图片，由此让其他图片获得油画风格。</p><p>先了解程序的依赖，有</p><ul><li>Python &gt;&#x3D; 2.7</li><li>CUDA &gt;&#x3D;6.5 （版本越高越好）</li><li>Caffe</li></ul><p>关于 CUDA（Compute Unified Device Architecture，统一计算架构），NVIDIA 开发的可以允许程序使用 NVIDIA 的 GPU 进行计算的技术。它可以让程序使用 GPU 进行计算，从而加快程序的运行速度。Caffe（Convolutional Architecture for Fast Feature Embedding） 是一个深度学习框架，Caffe 的 GPU 模式可以使用 CUDA 来进行计算。</p><p>另外，除了这三个需要安装的软件，还需要安装这三个软件所依赖的软件或者它们的扩展。它们分别是：</p><ul><li>OpenCV（开源计算机视觉库）</li><li>vcForPython27（用于编译 Python 扩展的专用编译器）</li></ul><p>Caffe 依赖 OpenCV，vcForPython27 是用于编译 Python 扩展的，如程序需要用到的 numPy 等，这些扩展中含有 C&#x2F;C++ 源码，需要使用编译器来编译。</p><h3 id="二、配置运行环境"><a href="#二、配置运行环境" class="headerlink" title="二、配置运行环境"></a>二、配置运行环境</h3><h4 id="（1）Python-2-7"><a href="#（1）Python-2-7" class="headerlink" title="（1）Python 2.7"></a>（1）Python 2.7</h4><p>首先安装 Python 2.7，在官网下载并安装即可。安装完成后将环境变量“PYTHONPATH”的值设置为 Python 的安装目录。</p><h4 id="（2）OpenCV"><a href="#（2）OpenCV" class="headerlink" title="（2）OpenCV"></a>（2）OpenCV</h4><p>可以直接将 cv2.pyd（Python Dynamic Module）文件放入 Python 安装目录的 DLLs 目录中即可，cv2.pyd 可以在<a href="/link/?t=S9PEQ0" target="_blank">这里</a>下载。</p><h4 id="（3）vcForPython27"><a href="#（3）vcForPython27" class="headerlink" title="（3）vcForPython27"></a>（3）vcForPython27</h4><p>安装编译 Python 扩展专用的编译器，官网下载地址点击<a href="/link/?t=0AS6HG" target="_blank">这里</a>，按照提示安装即可。</p><h4 id="（4）CUDA"><a href="#（4）CUDA" class="headerlink" title="（4）CUDA"></a>（4）CUDA</h4><p>从官方下载后按照提示安装即可。</p><h4 id="（5）Caffe"><a href="#（5）Caffe" class="headerlink" title="（5）Caffe"></a>（5）Caffe</h4><p>由于我是使用的是 Windows 10 系统，所以只记录如何在 Windows 平台安装 Caffe 了。Caffe 的 Windows 版本托管在 Github 上（<a href="/link/?t=D8NG8T" target="_blank">https://github.com/BVLC/caffe/tree/windows</a>），从 Github 项目的 README 说明中可以下载到已经编译好的可执行文件。将文件解压，并在“style-transfer”的 style.py 文件中的 import sys 语句下方添加这两句将 Caffe 的所在目录添加进 Python 搜索的路径中。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs python">sys.path.append(<span class="hljs-string">&quot;/path/to/caffe/python&quot;</span>)<br>sys.path.append(<span class="hljs-string">&quot;/path/to/caffe/python/caffe&quot;</span>)<br></code></pre></td></tr></table></figure><p>可以打开 Python 命令行输入 import sys 和上述两个语句后输入 import caffe 测试是否能够正常导入。</p><h4 id="（6）安装-Caffe-和-style-transfer-的依赖"><a href="#（6）安装-Caffe-和-style-transfer-的依赖" class="headerlink" title="（6）安装 Caffe 和 style-transfer 的依赖"></a>（6）安装 Caffe 和 style-transfer 的依赖</h4><p>打开命令行，分别进入 Caffe 和 style-transfer 所在的目录，使用如下命令编译安装 Caffe 的依赖。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">python -m pip install -r requirement.txt<br></code></pre></td></tr></table></figure><p>在我安装过程中出现了两个错误，一个是编译时提示找不到“stdint.h”，我在网上找到了这个文件并将它放在 vcForPython27 安装目录的 include 目录中就可以正常编译了，这个文件可以在<a href="/link/?t=2UATQ3" target="_blank">这里</a>找到。</p><p>第二个错误是在安装 Caffe 的依赖之一—— leveldb 的时候提示“Don’t know how to compile leveldb on Windows. ”。为了解决这个问题，我将一份编译好的 leveldb.pyd 文件直接放在了 Python 安装目录的 Lib\site-packages 目录中，并将 leveldb 从 Caffe 的 requirement.txt 文件中删去，在完成剩余的依赖安装。现在这个 leveldb.pyd 文件可以在<a href="/link/?t=QDOW81" target="_blank">这里</a>找到。</p><h3 id="三、运行-style-transfer"><a href="#三、运行-style-transfer" class="headerlink" title="三、运行 style-transfer"></a>三、运行 style-transfer</h3><p>运行环境配置完成之后就可以运行 style-transfer 来转移照片艺术风格了。先下载训练好的模型（caffemodel 文件），运行下面的命令来下载，要先安装有 bash 和 curl。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">bash scripts/download_models.sh &lt;model_name&gt;<br></code></pre></td></tr></table></figure><p>其中 <model_name> 是模型的名字。下载好模型后，运行以下命令来使用<code>&lt;model-name&gt;</code>模型将<code>&lt;style-image&gt;</code>图片的艺术风格转移到<code>&lt;content-image&gt;</code>指定的图片中，这个过程将使用 GPU 0（也就是设备的第一个GPU）来计算。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">python style.py -s &lt;style-image&gt; -c &lt;content-image&gt; -m &lt;model-name&gt; -g 0<br></code></pre></td></tr></table></figure><p>运行结果将会输出到程序运行结束后的提示路径中。</p><p>附上我自己的运行结果：</p><p>风格图片（style-image）梵高的《星夜》</p><p><img src="/img/style-starry-night.jpg" alt="梵高《星夜》"></p><p>内容图片（content-image）《Twilight Epiphany》</p><p><img src="/img/style-TwilightEpiphany_ZH-CN11612238738_1920x1080.jpg" alt="《Twilight Epiphany》"></p><p>输出图片（output-image）《星夜》风格的《Twilight Epiphany》</p><p><img src="/img/style-TwilightEpiphany_ZH-CN11612238738_1920x1080-starry_night-vgg19-content-1e4-512.jpg" alt="《星夜》风格的《Twilight Epiphany》"></p><p>以上。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;最近几天学习了一下如何使用 Github 上的项目“style-transfer”（&lt;a href=&quot;/link/?t=NSC9B0&quot; target=&quot;_blank&quot;&gt;https://github.com/fzliu/style-transfer&lt;/a&gt;）来转移照片的艺术风</summary>
      
    
    
    
    <category term="Computer Science" scheme="https://maphical.cn/categories/Computer-Science/"/>
    
    <category term="Machine Learning" scheme="https://maphical.cn/categories/Computer-Science/Machine-Learning/"/>
    
    
  </entry>
  
  <entry>
    <title>解决CENTOS下NGINX运行PHP显示FILE NOT FOUND的方法</title>
    <link href="https://maphical.cn/2017/11/deal-with-php-file-not-found/"/>
    <id>https://maphical.cn/2017/11/deal-with-php-file-not-found/</id>
    <published>2017-11-11T12:48:00.000Z</published>
    <updated>2025-10-11T16:10:29.448Z</updated>
    
    <content type="html"><![CDATA[<p>最近配置 LNMP 网站的时候遇到了 php-fpm 总是返回“File not found”的错误，Nginx 的日志文件中的记录是：</p><figure class="highlight livecodeserver"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs livecodeserver">FastCGI sent <span class="hljs-keyword">in</span> <span class="hljs-keyword">stderr</span>: <span class="hljs-string">&quot;Primary script unknown&quot;</span> <span class="hljs-keyword">while</span> reading response header <span class="hljs-built_in">from</span> upstream<br></code></pre></td></tr></table></figure><p>上网搜索解决方法，发现大部分这个问题都是由于脚本路径设置错误引起的。解决方法是：把 fastcgi_param 设置为：</p><figure class="highlight nginx"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs nginx"><span class="hljs-attribute">fastcgi_param</span> SCRIPT_FILENAME <span class="hljs-variable">$document_root</span><span class="hljs-variable">$fastcgi_script_name</span>;<br></code></pre></td></tr></table></figure><p>可是我明明已经像这样配置了 Nginx，更奇怪的是，有时 Nginx 的错误日志里会显示这样的错误：</p><figure class="highlight stata"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs stata">FastCGI sent <span class="hljs-keyword">in</span> stderr: &quot;Unable to <span class="hljs-keyword">open</span> primary script:<br>/<span class="hljs-keyword">var</span>/www/html/<span class="hljs-keyword">test</span>/index.php (<span class="hljs-keyword">No</span> such <span class="hljs-keyword">file</span> or directory)&quot;<br></code></pre></td></tr></table></figure><p>路径都显示出来了，这个文件明明是存在的啊，为什么显示没有此文件呢？找了很多网上的回答，找到了这一篇：</p><blockquote><p>SELinux will cause this error on CentOS&#x2F;RHEL 7+ by default 🙁</p><p>To test if SELinux is the source of your woes, do</p><p>setenforce 0</p><p>… and see if everything works. If that fixed it, you can leave SELinux off (weak, you’re better than that), or you can turn it back on with</p><p>setenforce 1</p><p>… and then properly fix the issue.</p><p>If you do</p><p>tail -f &#x2F;var&#x2F;log&#x2F;audit&#x2F;audit.log</p><p>… you’ll see the SELinux issue. In my case, it was denying PHP-FPM access to web files. You can run the following directives to fix it:</p><p>setsebool -P httpd_can_network_connect_db 1<br>setsebool -P httpd_can_network_connect 1</p><p>This actually didn’t fix it for me at first, but then restoring SELinux context did it</p><p>restorecon -R -v &#x2F;var&#x2F;www</p><p>Hope that helps.</p><p><em>作者：匿名用户</em><br><em>链接：<a href="https://www.zhihu.com/question/22128267/answer/105600681">https://www.zhihu.com/question/22128267/answer/105600681</a></em><br><em>来源：知乎</em><br><em>著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。</em></p></blockquote><p>就是执行“setenforce 0”来判断这个错误是否是 selinux 引起的（最好再执行“setenforce 1”来还原设置，可能是出于安全的原因？），如果是的话就执行</p><figure class="highlight bash"><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><code class="hljs bash">setsebool -P httpd_can_network_connect_db 1<br>setsebool -P httpd_can_network_connect 1<br>restorecon -R -v /var/www<br></code></pre></td></tr></table></figure><p>来解决问题，注意“&#x2F;var&#x2F;www”要换成网站文件所在的目录。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;最近配置 LNMP 网站的时候遇到了 php-fpm 总是返回“File not found”的错误，Nginx 的日志文件中的记录是：&lt;/p&gt;
&lt;figure class=&quot;highlight livecodeserver&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;g</summary>
      
    
    
    
    <category term="Computer Science" scheme="https://maphical.cn/categories/Computer-Science/"/>
    
    <category term="Operation &amp; Maintenance" scheme="https://maphical.cn/categories/Computer-Science/Operation-Maintenance/"/>
    
    
  </entry>
  
  <entry>
    <title>LNMP架构网站的搭建</title>
    <link href="https://maphical.cn/2017/10/setup-lnmp-arch-website/"/>
    <id>https://maphical.cn/2017/10/setup-lnmp-arch-website/</id>
    <published>2017-10-24T11:50:02.000Z</published>
    <updated>2025-10-12T14:54:46.495Z</updated>
    
    <content type="html"><![CDATA[<p>因为最近接手了学校社团的网站运维工作，并且社团的网站使用的是 LNMP 架构，所以为了学习一下如何部署、配置和运维 LNMP 架构的网站，我把自己的个人网站（也就是这个网站），重新部署成了 LNMP 架构的网站。</p><p>在使用 Nginx 之前，我是使用 xampp 的，xampp 将 Apache、MySQL、PHP 和 Perl 集成并都配置好了放在一起，而且还有 phpmyadmin 提供图形化的数据库操作页面，运行 xampp security 命令就能自动帮你完善安全配置等等，可以说是开箱即用的网站搭建工具。而转向使用 Nginx 以后，我就要自己手工配置各个组件让他们协调运行了，想要部署一个有一定安全性的网站，对于我来说还是需要学习一些新知识的。</p><p>下面我就介绍一下我是如何搭建的。</p><h3 id="一、安装需要的软件"><a href="#一、安装需要的软件" class="headerlink" title="一、安装需要的软件"></a>一、安装需要的软件</h3><p>第一步肯定是要知道搭建 LNMP 架构网站需要哪些软件包了，具体看网站具有哪些功能，需要什么程序来提供这些功能。在这里，我需要安装 Nginx、MySQL 和 PHP 三个软件包。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><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><code class="hljs bash"><span class="hljs-built_in">sudo</span> apt-get install nginx<br><span class="hljs-built_in">sudo</span> apt-get install mysql-server<br><span class="hljs-built_in">sudo</span> apt-get install php7.0-fpm<br><span class="hljs-built_in">sudo</span> apt-get install php7.0-mysql<br></code></pre></td></tr></table></figure><p>MySQl 安装时会显示配置页面，输入初始密码等，并且默认只能本地访问，如果需要更改可以编辑 &#x2F;etc&#x2F;mysql&#x2F;mysql.conf.d&#x2F;mysqld.cnf 文件。php-fpm（PHP FastCGI Process Manager）在架构中扮演的角色是用于解析 PHP 脚本的后端服务器，php-mysql 是 PHP 软件包的扩展，用于提供 MySQL 数据库连接支持。需要注意的是，要启用 PHP 的 MySQL 支持，需要编辑 &#x2F;etc&#x2F;php&#x2F;7.0&#x2F;fpm&#x2F;php.ini 文件，将其中的这句前面的分号（注释）去掉，如下：</p><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ini"><span class="hljs-attr">extension</span>=php_mysqli.dll<br></code></pre></td></tr></table></figure><p>使用 service 命令可以控制以上这些软件包启动（start）、重启（restart）、重新加载设置（reload）、停止（stop）、设置检查（configtest，部分命令支持此选项）。</p><p>如需要启动 php-fpm 服务，命令行输入下面的命令：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">sudo</span> service php7.0-fpm start<br></code></pre></td></tr></table></figure><p>重新加载设置选项对于配置 Nginx 服务器很常用，当我们在 &#x2F;etc&#x2F;nginx&#x2F; 目录中配置了虚拟主机或者更改了设置以后，就需要使用 reload 命令来让 Nginx 重新加载配置使它们生效，如下：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">sudo</span> service nginx reload<br></code></pre></td></tr></table></figure><p>软件包全都安装好之后需要配置它们才能相互配合协同工作。</p><h3 id="二、配置相应的软件"><a href="#二、配置相应的软件" class="headerlink" title="二、配置相应的软件"></a>二、配置相应的软件</h3><p>在配置之前需要了解此架构中各个软件各自扮演了什么角色，从而更合理地配置和更容易地发现和解决问题。</p><p>LNMP 中，Nginx 是作为一个反向代理服务器，用于分发静态内容和传递收到的动态访问请求到后端的 PHP FastCGI 进程；PHP FastCGI 进程运行 PHP 脚本，连接 MySQL 服务器等。既然要让它们能够相互通信，那么就要配置相应的通信端口，如果使用了防火墙，也可以直接使用 UNIX 套接字通信。</p><h4 id="1-配置-PHP"><a href="#1-配置-PHP" class="headerlink" title="1.配置 PHP"></a>1.配置 PHP</h4><p>首先配置 PHP 吧，编辑 &#x2F;etc&#x2F;php&#x2F;7.0&#x2F;fpm&#x2F;pool.d&#x2F;<a href="http://www.conf/">www.conf</a> 文件，设置进程监听的端口（或者 socket）。这里我使用了默认的 socket 作为通信方式，有如下语句：</p><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ini"><span class="hljs-attr">listen</span> = /path/to/your/socket<br></code></pre></td></tr></table></figure><p>配置 PHP 的运行用户和组，如 www-data，有：</p><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs ini"><span class="hljs-attr">user</span> = www-data<br><span class="hljs-attr">group</span> = www-data<br></code></pre></td></tr></table></figure><p>配置允许连接的 IP 地址（只允许本地连接请求）：</p><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ini"><span class="hljs-attr">listen.allowed_clients</span> = <span class="hljs-number">127.0</span>.<span class="hljs-number">0.1</span><br></code></pre></td></tr></table></figure><p>接着保存退出，运行：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">sudo</span> service php7.0-fpm reload<br></code></pre></td></tr></table></figure><p>重新加载设置，若 PHP 还没有启动，就执行 start 指令启动。</p><h4 id="2-配置-MySQL"><a href="#2-配置-MySQL" class="headerlink" title="2.配置 MySQL"></a>2.配置 MySQL</h4><p>先查看 MySQL 服务器监听的端口号，并在防火墙中开启这个端口（默认为 3306）。</p><p>绑定 MySQL 的 IP 地址（本地）：</p><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ini"><span class="hljs-attr">bind-address</span> = <span class="hljs-number">127.0</span>.<span class="hljs-number">0.1</span><br></code></pre></td></tr></table></figure><p>另外的可访问的用户和密码已经在安装软件包时设置过了。</p><h4 id="3-设置-Nginx"><a href="#3-设置-Nginx" class="headerlink" title="3.设置 Nginx"></a>3.设置 Nginx</h4><p>如果网站运行的是 WordPress（比如我），可以查看 WordPress 的<a href="/link/?t=5A1JGD" target="_blank">官方 Nginx 配置指导</a>。虽然有官方指导了，我还是简单介绍一下吧，作为笔记。</p><p>首先，进入 &#x2F;etc&#x2F;nginx&#x2F;sites-available 目录下，这个目录是用来保存架设在 Nginx 上的虚拟主机配置文件的，所有已启用或未启用的主机配置文件都放在这个目录中。新建一个文件，比如 wordpress，编辑这个文件，在文件中写一个 server 块（一个虚拟主机），大致内容为：</p><figure class="highlight nginx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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><code class="hljs nginx"><span class="hljs-section">server</span> &#123;<br> <span class="hljs-attribute">listen</span> <span class="hljs-number">443</span> default_server;<br> <span class="hljs-attribute">listen</span> [::]:<span class="hljs-number">443</span> default_server;<br><br> <span class="hljs-attribute">ssl</span> <span class="hljs-literal">on</span>;<br> <span class="hljs-attribute">ssl_certificate</span> /path/to/your/certificate;<br> <span class="hljs-attribute">ssl_certificate_key</span> /path/to/your/certificate;<br><span class="hljs-comment"># ... 其它自定义的配置内容</span><br><br> <span class="hljs-attribute">server_name</span> example.com;<br> <span class="hljs-attribute">index</span> index.html index.php;<br> <span class="hljs-attribute">root</span> /var/www/html/example;<br><br> <span class="hljs-attribute">include</span> snippets/restruction.conf; <span class="hljs-comment"># 这里标注一下</span><br><br><span class="hljs-section">location</span> / &#123;<br> <span class="hljs-attribute">try_files</span> <span class="hljs-variable">$uri</span> <span class="hljs-variable">$uri</span>/ /index.php?<span class="hljs-variable">$args</span>;<br> &#125;<br><br><span class="hljs-section">location</span> <span class="hljs-regexp">~ \.php$</span> &#123;<br> <span class="hljs-comment">#<span class="hljs-doctag">NOTE:</span> You should have &quot;cgi.fix_pathinfo = 0;&quot; in php.ini</span><br> <span class="hljs-attribute">include</span> snippets/fastcgi-php.conf;<br><span class="hljs-comment"># ... 其它自定义的配置内容</span><br> <span class="hljs-attribute">fastcgi_pass</span> unix:/path/to/your/php/socket;<br> &#125;<br><br><span class="hljs-section">location</span> <span class="hljs-regexp">~* \.(js|css|png|jpg|jpeg|gif|ico)$</span> &#123;<br> <span class="hljs-attribute">expires</span> max;<br> <span class="hljs-attribute">log_not_found</span> <span class="hljs-literal">off</span>;<br> &#125;<br><br><span class="hljs-comment"># ... 其它自定义的配置内容</span><br>&#125;<br></code></pre></td></tr></table></figure><p>这里的虚拟主机监听 443 端口（https 端口），如果没有证书或者不想使用 https 那就把 443 改成 80，把 ssl 那部分都删掉就行了。如果像让访问者从 http 自动跳转为 https，可以再在以上配置的基础上添加一个 80 端口的虚拟主机，将访问此主机的 http 请求重写为 https。</p><p>server_name 是主机的域名（包括主机名），请求传入之后 Nginx 会判断域名并将 URL 交给相应的虚拟主机处理。如 <a href="https://test.example.com/index.html">https://test.example.com/index.html</a> 这样的 URL，Nginx 会传递给 server_name 为 test.example.com 的虚拟主机处理。</p><p>index 是指当 URI 没有指定访问的文件时，默认访问的文件名。root 是指网站的根目录，即“&#x2F;”路径所指的目录。</p><p>location &#x2F; {…} 这一段的意思是：当 URI 匹配到 &#x2F; （根目录）时，尝试 URI 能否访问，若不能，就尝试 URI 后加一个“&#x2F;”访问，若这两个都不能访问，那么访问“&#x2F;index.php?$args”这个路径。</p><p>location ~ .php$ {…} 这一段用于执行 PHP 脚本，当 URI 匹配到“.php$”（以 php 结尾的 URI）时，就将此请求传递给 PHP FastCGI 进程来运行脚本。</p><p>关于配置中所标注的那一句“include snippets&#x2F;restruction.conf;”，是 WordPress 官方所建议的为 WordPress 或者其他网站所添加的安全配置，添加这个配置可以禁止通过 URL 访问上传的文件，主要是为了防止有人执行使用者上传的脚本文件等。</p><h3 id="三、配置完成"><a href="#三、配置完成" class="headerlink" title="三、配置完成"></a>三、配置完成</h3><p>配置完成后就可以通过命令启动或重新启动所有服务让设置生效：</p><figure class="highlight bash"><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><code class="hljs bash"><span class="hljs-built_in">sudo</span> service php7.0-fpm start|restart|reload <span class="hljs-comment"># 启动|重启|重新加载配置</span><br><span class="hljs-built_in">sudo</span> service mysql start|restart|reload <span class="hljs-comment"># 同上</span><br><span class="hljs-built_in">sudo</span> service nginx start|restart|reload <span class="hljs-comment"># 同上</span><br></code></pre></td></tr></table></figure><p>这样网站应该就能访问了，如果有提高网站访问速度的需求，可以将网站接入 CDN，这样既提高了访问速度，也增加了一定的安全性。</p><p>以上。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;因为最近接手了学校社团的网站运维工作，并且社团的网站使用的是 LNMP 架构，所以为了学习一下如何部署、配置和运维 LNMP 架构的网站，我把自己的个人网站（也就是这个网站），重新部署成了 LNMP 架构的网站。&lt;/p&gt;
&lt;p&gt;在使用 Nginx 之前，我是使用 xampp</summary>
      
    
    
    
    <category term="Computer Science" scheme="https://maphical.cn/categories/Computer-Science/"/>
    
    <category term="Architectures &amp; Principles" scheme="https://maphical.cn/categories/Computer-Science/Architectures-Principles/"/>
    
    <category term="Website" scheme="https://maphical.cn/categories/Computer-Science/Architectures-Principles/Website/"/>
    
    
  </entry>
  
  <entry>
    <title>SQL常用语句</title>
    <link href="https://maphical.cn/2017/10/useful-sql/"/>
    <id>https://maphical.cn/2017/10/useful-sql/</id>
    <published>2017-10-14T14:26:17.000Z</published>
    <updated>2025-10-11T16:10:29.448Z</updated>
    
    <content type="html"><![CDATA[<p>在很多地方都需要操作数据库，了解一些 SQL 语句有时十分必要，对于不是专门搞数据库的人（比如我）来说，会一些基本操作就行了，比如选取数据、插入数据、更新数据、删除数据等。这是一篇记录 SQL 常用操作的笔记。</p><h2 id="1-选取数据（SELECT）："><a href="#1-选取数据（SELECT）：" class="headerlink" title="1.选取数据（SELECT）："></a>1.选取数据（SELECT）：</h2><p>SELECT 语句用于从表中选取数据，结果以另一个表的形式被打印出来。</p><p><strong>语法：</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs SQL"><span class="hljs-keyword">SELECT</span> 字段名 <span class="hljs-keyword">FROM</span> 表名称 (<span class="hljs-keyword">WHERE</span> 条件);<br></code></pre></td></tr></table></figure><p>其中 WHERE 条件是可选的，如我需要从 test 表中选取 value 字段值为 1 的条目的 name 字段值，就要如下的语句：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs SQL"><span class="hljs-keyword">SELECT</span> name <span class="hljs-keyword">FROM</span> test <span class="hljs-keyword">WHERE</span> <span class="hljs-keyword">value</span><span class="hljs-operator">=</span><span class="hljs-number">1</span>;<br></code></pre></td></tr></table></figure><h2 id="2-插入数据（INSERT-INTO）："><a href="#2-插入数据（INSERT-INTO）：" class="headerlink" title="2.插入数据（INSERT INTO）："></a>2.插入数据（INSERT INTO）：</h2><p>INSERT 语句用于向表中插入新的行。</p><p><strong>语法：</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs SQL"><span class="hljs-keyword">INSERT INTO</span> 表名称 <span class="hljs-keyword">VALUES</span> (值<span class="hljs-number">1</span>, 值<span class="hljs-number">2</span>, ...);<br></code></pre></td></tr></table></figure><p>如我需要向 test 表中插入 value 字段值为 1，name 字段值为 Ben 的行，我应该如下写（假如 name 字段在 value 字段前面）：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs SQL"><span class="hljs-keyword">INSERT INTO</span> test <span class="hljs-keyword">VALUES</span> (&quot;Ben&quot;, <span class="hljs-number">1</span>);<br></code></pre></td></tr></table></figure><p>其中值的顺序是有讲究的，要和表中字段的顺序一致，此语句会按照语句中值的顺序依次向表中不同字段写入数据。这里将“Ben”写在 1 的前面是因为我假设了表中 name 字段在 value 字段的前面。</p><h2 id="3-修改数据（UPDATE）："><a href="#3-修改数据（UPDATE）：" class="headerlink" title="3.修改数据（UPDATE）："></a>3.修改数据（UPDATE）：</h2><p>UPDATE 语句用于修改表中的数据。</p><p><strong>语法：</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs SQL"><span class="hljs-keyword">UPDATE</span> 表名称 <span class="hljs-keyword">SET</span> 字段名 <span class="hljs-operator">=</span> 新值 <span class="hljs-keyword">WHERE</span> 条件;<br></code></pre></td></tr></table></figure><p>假如表 test 中有一行数据为：name&#x3D;”Ben”, value&#x3D;1，我要把其中的 value 修改为 2，那么我要如下写：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs SQL"><span class="hljs-keyword">UPDATE</span> test <span class="hljs-keyword">SET</span> <span class="hljs-keyword">value</span> <span class="hljs-operator">=</span> <span class="hljs-number">2</span> <span class="hljs-keyword">WHERE</span> name <span class="hljs-operator">=</span> &quot;Ben&quot;;<br></code></pre></td></tr></table></figure><h2 id="4-删除数据（DELETE）："><a href="#4-删除数据（DELETE）：" class="headerlink" title="4.删除数据（DELETE）："></a>4.删除数据（DELETE）：</h2><p>DELETE 语句用于删除表中的行。</p><p><strong>语法：</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs SQL"><span class="hljs-keyword">DELETE</span> <span class="hljs-keyword">FROM</span> 表名称 （<span class="hljs-keyword">WHERE</span> 条件）;<br></code></pre></td></tr></table></figure><p>其中条件是可选的，如果不写条件则会删除表中的所有数据。假如我要删除 test 表中字段 name 值为 “Ben” 的一行数据，那么我要如下写：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs SQL"><span class="hljs-keyword">DELETE</span> <span class="hljs-keyword">FROM</span> test <span class="hljs-keyword">WHERE</span> name <span class="hljs-operator">=</span> &quot;Ben&quot;;<br></code></pre></td></tr></table></figure><h2 id="5-创建数据库（CREATE-DATABASE）："><a href="#5-创建数据库（CREATE-DATABASE）：" class="headerlink" title="5.创建数据库（CREATE DATABASE）："></a>5.创建数据库（CREATE DATABASE）：</h2><p>CREATE DATABASE 语句用于创建一个新的数据库。</p><p><strong>语法：</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs SQL"><span class="hljs-keyword">CREATE</span> DATABASE 数据库名;<br></code></pre></td></tr></table></figure><p>登入数据库以后，假如我要创建一个名为 test 的数据库，那么我要如下写：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs SQL"><span class="hljs-keyword">CREATE</span> DATABASE test;<br></code></pre></td></tr></table></figure><p>创建数据库以后使用 USE 语句选取数据库进行操作，语法如下：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs SQL">USE 数据库名;<br></code></pre></td></tr></table></figure><p>选取成功后会有提示，如“DATABASE CHANGED”等，这时就可以对数据库中的表进行前面所说的 4 种操作了（写的顺序好像有点不对？）。</p><h2 id="6-删除数据库（DROP-DATABASE）："><a href="#6-删除数据库（DROP-DATABASE）：" class="headerlink" title="6.删除数据库（DROP DATABASE）："></a>6.删除数据库（DROP DATABASE）：</h2><p>DROP DATABASE 语句用于删除一个数据库和它里面的所有表。</p><p><strong>语法：</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs SQL"><span class="hljs-keyword">DROP</span> DATABASE 数据库名;<br></code></pre></td></tr></table></figure><h2 id="7-创建表（CREATE-TABLE）："><a href="#7-创建表（CREATE-TABLE）：" class="headerlink" title="7.创建表（CREATE TABLE）："></a>7.创建表（CREATE TABLE）：</h2><p>CREATE TABLE 语句用于创建一张新表。</p><p><strong>语法：</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs SQL"><span class="hljs-keyword">CREATE TABLE</span> 表名 ( 字段名 数据类型, 字段名 数据类型... );<br></code></pre></td></tr></table></figure><p>如，我想创建一张名为 test 的，有字符类型名为 id，整数类型名为 num 的两个字段的表，就如下写：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs SQL"><span class="hljs-keyword">CREATE TABLE</span> test ( id <span class="hljs-type">VARCHAR</span>(<span class="hljs-number">255</span>), num <span class="hljs-type">INT</span> );<br></code></pre></td></tr></table></figure><p>更多的数据类型查看 MySQL 官网。</p><h2 id="8-删除表（DROP-TABLE）："><a href="#8-删除表（DROP-TABLE）：" class="headerlink" title="8.删除表（DROP TABLE）："></a>8.删除表（DROP TABLE）：</h2><p>DROP TABLE 语句用于删除表。</p><p><strong>语法：</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs SQL"><span class="hljs-keyword">DROP</span> <span class="hljs-keyword">TABLE</span> 表名;<br></code></pre></td></tr></table></figure><p>删除一个表会删除里面的所有数据。</p><h2 id="9-列出表（SHOW-TABLES）："><a href="#9-列出表（SHOW-TABLES）：" class="headerlink" title="9.列出表（SHOW TABLES）："></a>9.列出表（SHOW TABLES）：</h2><p>SHOW TABLES 语句用于列出当前数据库中的所有表。</p><p><strong>语法：</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs SQL"><span class="hljs-keyword">SHOW</span> TABLES;<br></code></pre></td></tr></table></figure><h2 id="10-从本地文件导入数据（LOAD-DATA-LOCAL-INFILE）"><a href="#10-从本地文件导入数据（LOAD-DATA-LOCAL-INFILE）" class="headerlink" title="10.从本地文件导入数据（LOAD DATA LOCAL INFILE）"></a>10.从本地文件导入数据（LOAD DATA LOCAL INFILE）</h2><p>由于安全考虑，从本地文件导入数据是默认被禁用的，要启用这个功能，需要在启动  MySQL 客户端的时候加上启动参数 –local-infile ，如：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs SQL">mysql <span class="hljs-operator">-</span>uroot <span class="hljs-operator">-</span>p <span class="hljs-comment">--local-infile</span><br></code></pre></td></tr></table></figure><p>LOAD DATA LOCAL INFILE 语句用于本地文件中导入数据。</p><p><strong>语法：</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs SQL">LOAD DATA <span class="hljs-keyword">LOCAL</span> INFILE <span class="hljs-string">&#x27;本地文件路径&#x27;</span> <span class="hljs-keyword">INTO</span> <span class="hljs-keyword">TABLE</span> 表名;<br></code></pre></td></tr></table></figure><p>如，我想从 &#x2F;data&#x2F;data.txt 文件导入数据到 test 表中，文件中的数据之间可以用空格分隔，文件内容如：</p><figure class="highlight avrasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs avrasm"><span class="hljs-symbol">Line1:</span>  <span class="hljs-string">&quot;String&quot;</span>    <span class="hljs-number">1</span>(\r\n)<br><span class="hljs-symbol">Line2:</span>  ...<br></code></pre></td></tr></table></figure><p>其中 \r\n 是 win 格式的换行符，用换行符表名一个条目的结束，数据的顺序要和表中字段的顺序相同。导入命令为：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs SQL">LOAD <span class="hljs-keyword">LOCAL</span> DATA INFILE <span class="hljs-string">&#x27;/data/data.txt&#x27;</span> <span class="hljs-keyword">INTO</span> <span class="hljs-keyword">TABLE</span> test;<br></code></pre></td></tr></table></figure><h2 id="11-创建用户（CREATE-USER）"><a href="#11-创建用户（CREATE-USER）" class="headerlink" title="11.创建用户（CREATE USER）"></a>11.创建用户（CREATE USER）</h2><p>CREATE USER 语句用于创建一个新用户。</p><p><strong>语法：</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs SQL"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">USER</span> <span class="hljs-string">&#x27;用户名&#x27;</span>@<span class="hljs-string">&#x27;用户所在的IP地址&#x27;</span> IDENTIFIED <span class="hljs-keyword">BY</span> <span class="hljs-string">&#x27;密码&#x27;</span>;<br></code></pre></td></tr></table></figure><p>上面“用户所在的 IP 地址”是指允许此 IP 地址登录此用户。如：’test’@’localhost’ 是指只允许来自本地的连接登录 test 用户。另外，‘%’ 百分号表示任意 IP 地址，即此用户接受来自任何 IP 的登录。</p><p>密码在设置的时候可能会受到“当前数据库所允许的密码安全等级”的限制，如果不使用符合当前密码要求的密码进行设置，就会报错无法创建用户。</p><h2 id="12-更改用户权限（GRANT）"><a href="#12-更改用户权限（GRANT）" class="headerlink" title="12.更改用户权限（GRANT）"></a>12.更改用户权限（GRANT）</h2><p>GRANT  语句用于更改用户对数据库的操作权限。</p><p><strong>语法：</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs SQL"><span class="hljs-keyword">GRANT</span> 操作名称 <span class="hljs-keyword">ON</span> 操作对象 <span class="hljs-keyword">TO</span> <span class="hljs-string">&#x27;用户名&#x27;</span>@<span class="hljs-string">&#x27;用户所在的IP地址&#x27;</span>;<br></code></pre></td></tr></table></figure><p>此命令可以将对“操作对象”的“操作名称”权限授权给此用户，如：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs SQL"><span class="hljs-keyword">GRANT</span> <span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">ON</span> test.<span class="hljs-operator">*</span> <span class="hljs-keyword">TO</span> <span class="hljs-string">&#x27;test&#x27;</span>@<span class="hljs-string">&#x27;localhost&#x27;</span>;<br></code></pre></td></tr></table></figure><p>就将对 test 数据库中所有对象的插入操作权限授权给了 ‘test’@’localhost’ 用户。注意，操作对象的格式为“数据库.数据库中的对象”，其中“<em>”星号表示所有对象，“</em>.*”代表所有数据库的所有对象。还有，用户是指用户名加上 IP 地址，若用户名相同但 IP 地址不同，会被看作为两个不同的用户。</p><h2 id="13-删除用户（DROP-USER）"><a href="#13-删除用户（DROP-USER）" class="headerlink" title="13.删除用户（DROP USER）"></a>13.删除用户（DROP USER）</h2><p>DROP USER 语句用于删除用户。</p><p><strong>语法：</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs SQL"><span class="hljs-keyword">DROP</span> <span class="hljs-keyword">USER</span> <span class="hljs-string">&#x27;用户名&#x27;</span>@<span class="hljs-string">&#x27;用户所在的IP地址&#x27;</span>;<br></code></pre></td></tr></table></figure><p>这样就能删除现有的用户了。</p><p>以上。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;在很多地方都需要操作数据库，了解一些 SQL 语句有时十分必要，对于不是专门搞数据库的人（比如我）来说，会一些基本操作就行了，比如选取数据、插入数据、更新数据、删除数据等。这是一篇记录 SQL 常用操作的笔记。&lt;/p&gt;
&lt;h2 id=&quot;1-选取数据（SELECT）：&quot;&gt;&lt;a</summary>
      
    
    
    
    <category term="Computer Science" scheme="https://maphical.cn/categories/Computer-Science/"/>
    
    <category term="Database" scheme="https://maphical.cn/categories/Computer-Science/Database/"/>
    
    <category term="MySQL(MariaDB)" scheme="https://maphical.cn/categories/Computer-Science/Database/MySQL-MariaDB/"/>
    
    
  </entry>
  
  <entry>
    <title>C中PRINTF和SCANF的一些知识</title>
    <link href="https://maphical.cn/2017/09/something-about-scanf-in-c/"/>
    <id>https://maphical.cn/2017/09/something-about-scanf-in-c/</id>
    <published>2017-09-09T03:20:08.000Z</published>
    <updated>2025-10-11T16:10:29.448Z</updated>
    
    <content type="html"><![CDATA[<p>暑假在家看《C Primer Plus》，有一部分对 printf 和 scanf 函数讲得比较细，所以想要写一篇笔记记录一下这些知识。</p><h3 id="一、关于-printf-函数"><a href="#一、关于-printf-函数" class="headerlink" title="一、关于 printf 函数"></a>一、关于 printf 函数</h3><p>printf 和 scanf 函数是 C 语言中的输入&#x2F;输出函数（I&#x2F;O 函数），可以使用这两个函数来实现程序和用户的交互。也是 C 语言初学者所接触到的最早的两个函数了，printf 函数用于格式化输出信息，我们最早会用这个函数来编写“Hello world”程序，想必对于 C 语言的学习者来说是最熟悉不过的函数了。</p><h4 id="1-函数的参数格式"><a href="#1-函数的参数格式" class="headerlink" title="1.函数的参数格式"></a>1.函数的参数格式</h4><p>printf 函数与一般我们定义的函数不同，它是没有固定参数个数的。它使用“格式字符串+参数列表”的形式获取参数，即“格式字符串”是 printf 的第一个参数，其中包含了后续参数列表中的参数个数和输出信息的格式等信息。如：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;My name is %s, %d years old.&quot;</span>, name, age);<br></code></pre></td></tr></table></figure><p>上面的“My name is %s, %d years old.”就是格式字符串，而后面跟着的 name 和 age 是需要打印其值的变量，这两个变量的值分别替换字符串中的“%s”和“%d”，然后 printf 函数将替换后的文本打印到控制台中的当前位置。由此可见，printf 函数可以通过格式字符串中的“%s”等（转换说明）获取到参数的数量并加以处理。</p><p>格式字符串后面的参数列表中的参数可以是变量、常量或者是需要在打印之前先计算其值的表达式。格式字符串中应该包含“实际需要打印的字符”和“转换说明”两种信息。若格式字符串中没有转换说明，那么也可以没有参数列表，如：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;My name is Ming, 16 years old&quot;</span>);<br></code></pre></td></tr></table></figure><p>这样即表明格式字符串中都是实际需要打印的字符，没有需要替换的转换说明，printf 将会把格式字符串中的字符串原样打印到控制台的当前位置。</p><h4 id="2-函数的转换说明"><a href="#2-函数的转换说明" class="headerlink" title="2.函数的转换说明"></a>2.函数的转换说明</h4><p>上一段中提到了“转换说明”，就是在格式字符串中类似于“%s”的字符组合。它在格式字符串中起到了“占位符”的作用，即标记了待插入内容将要插入到字符串中的位置，并表明了待插入内容的数据格式。printf 函数将转换说明和后面参数列表中的参数一一对应，一个转换说明对应一个参数，使用转换说明表明的数据格式读取参数（变量或者常量）中的值，将以二进制格式存储在计算机中的数据转换成一系列字符串，并将转换说明用读取出来的值替换掉，再进行下一个参数的读取和插入，直到转换说明都被替换掉为止。</p><p>即在上一个例子中，</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;My name is %s, %d years old.&quot;</span>, name, age);<br></code></pre></td></tr></table></figure><p>printf 先读取到了格式字符串，了解到了在格式字符串后面应该有两个参数用于替换转换说明，“%s”表明了后面参数列表中的第一个参数是字符串，所以 printf 函数用字符串的数据格式将 name 变量中的值读取出来，并用这个值替换了“%s”。按照这样的方式也替换了“%d”，如果还有更多的转换说明，那么以此类推都替换掉。</p><p>如果上面的 name 变量的值为“Ming”，age 的值为 16，那么被替换过的字符串就变成了“My name is Ming, 16 years old.”并被打印到控制台的当前位置。</p><p>有很多不同的转换说明支持转换不同类型的数据（这里“转换”的意思就是将以二进制格式存储在内存中的数据“翻译”成字符串并插入格式字符串中），常用的有以下一些：</p><table><thead><tr><th>转换说明</th><th>输出</th></tr></thead><tbody><tr><td>%a</td><td>浮点数、十六进制数和 p 计数法</td></tr><tr><td>%c</td><td>单个字符</td></tr><tr><td>%d</td><td>有符号十进制数</td></tr><tr><td>%f</td><td>浮点数，十进制记数法</td></tr><tr><td>%e</td><td>浮点数，e 计数法</td></tr><tr><td>%o</td><td>无符号八进制数</td></tr><tr><td>%p</td><td>指针</td></tr><tr><td>%s</td><td>字符串</td></tr><tr><td>%u</td><td>无符号十进制数</td></tr><tr><td>%x</td><td>无符号十六进制数（小写）</td></tr><tr><td>%%</td><td>打印一个百分号</td></tr></tbody></table><p>另外，格式字符串中的转换说明一定要与后面的参数数据类型相匹配，若转换说明个数和参数个数不同或者转换说明与参数类型不匹配，会导致不确定的后果（如打印出无意义的值或者程序停止运行等）。和转换说明一起使用的还有“转换说明修饰符”，用于修饰转换说明，常用的几个如下：</p><table><thead><tr><th>修饰符</th><th>含义</th></tr></thead><tbody><tr><td>数字</td><td>最小字段宽度</td></tr><tr><td>.数字</td><td>精度，%e或%f表示小数点后面位数</td></tr><tr><td>h</td><td>与整型说明一起使用表示short类型</td></tr><tr><td>hh</td><td>与整型说明一起使用表示char类型</td></tr><tr><td>l</td><td>与整型说明一起使用表示long类型</td></tr><tr><td>ll</td><td>与……一起使用表示long long类型</td></tr><tr><td>L</td><td>与浮点说明使用表long double类型</td></tr><tr><td>z</td><td>与整型说明一起使用表示size_t类型</td></tr><tr><td>-</td><td>待打印项左对齐</td></tr><tr><td>+</td><td>显示正负号</td></tr><tr><td>空格</td><td>正数显示空格，负数显示负号</td></tr><tr><td>#</td><td>使用能表明数据进制的形式显示</td></tr><tr><td>0</td><td>对于数值，使用0代替空格填充宽度</td></tr></tbody></table><h4 id="3-函数的打印机制和其返回值"><a href="#3-函数的打印机制和其返回值" class="headerlink" title="3.函数的打印机制和其返回值"></a>3.函数的打印机制和其返回值</h4><p>有时候即使用对了大部分转换说明也可能会出现下面这种情况：</p><p><img src="/img/scanf-stack1.jpg" alt="一段程序"></p><p>这段程序中 printf 虽然用对了 n3 和 n4 的转换说明，但是 n3 和 n4 还是没有被正确打印出来。其中的原因就在 printf 的参数传递过程和打印机制中。</p><p>以上代码在调用 printf 函数的时候，计算机根据程序中声明的变量类型将传递给函数的参数变量 n1， n2，n3，n4存入一个被称为“栈（stack）”的内存区域内，每个变量在存储时在内存中都是相邻的，如图：</p><p><img src="/img/scanf-stack2.jpg" alt="stack 示意图"></p><p>在栈中，n1 和 n2 各占用 8 字节，n3 和 n4 各占用 4 字节，4 个变量相邻地存储在栈中。</p><p>当 printf 开始按照所给的转换说明在栈中读取变量时，它先按照第一个转换说明“%ld”在栈中从头开始读取了 4 字节（n1 的完整数据应该是 8 字节），即读取了 n1 变量的一半（第一个方块）按照 long 类型解释，于是显示了一个错误的值。</p><p>接着 printf 按照第二个转换说明接着读取 4 字节的内容（第二个方块），再次错误地翻译了二进制数据并显示了；接下来根据第三个转换说明从当前栈中的位置继续读取 4 字节内容，又错误地显示了……</p><p>因为 printf 每次从栈中读取数据都是从当前位置读取，所以当第一个转换说明错误以后，printf 在第一次读取数据时读错了字节，再次读取时就“错位”了，因此也就不能正确的显示哪怕是正确的转换说明了。</p><p>printf 函数返回成功打印字符的个数，这个特性可以用来检查是否成功打印字符。若输出错误，printf 函数会返回一个负值。</p><h4 id="4-打印较长的字符串"><a href="#4-打印较长的字符串" class="headerlink" title="4.打印较长的字符串"></a>4.打印较长的字符串</h4><p>如果 printf 要打印较长的字符串，会导致语句太长不方便阅读。因为在 C 语言中使用空白字符（空格、制表符和换行符等）分隔不同的代码部分时，编译器会忽略它们，所以一条 printf 语句可以写成多行的形式。如参数与参数之间换行：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;Something long to print, something %s long to print, %s&quot;</span>,<br>    sth1, sth2);<br></code></pre></td></tr></table></figure><p>但是不能在格式字符串中间换行，这样会导致字符串中包含非法字符。</p><p>《C Primer Plus》中写道如果想要在字符串中换行，有以下三种方法：</p><ul><li>使用多个 printf 语句；</li><li>在字符串中使用“\”反斜杠加 Enter 键换行；</li><li>ANSI C 引入的字符串连接，使用两个带双引号字符串之间带空白隔开，编译器会将它们链接为一个字符串。</li></ul><p>例：</p><p><img src="/img/scanf-printflongstr.jpg" alt="printf 长字符串三种形式"></p><h3 id="二、关于-scanf-函数"><a href="#二、关于-scanf-函数" class="headerlink" title="二、关于 scanf 函数"></a>二、关于 scanf 函数</h3><p>C 语言包含很多输入函数，除了从标准输入输入，还有能够从文件读入数据的函数等。但在控制台和用户交互时，scanf 是比较通用的一个，因为它可以格式化地读取用户输入的数据。scanf 函数的功能是将读取的字符串转换成整数、浮点数、字符或字符串等，与 printf 的功能相反。</p><h4 id="1-函数的参数格式和转换说明"><a href="#1-函数的参数格式和转换说明" class="headerlink" title="1.函数的参数格式和转换说明"></a>1.函数的参数格式和转换说明</h4><p>scanf 函数的参数格式和 printf 函数相似，也使用“格式字符串+参数列表”的形式。格式字符串中也使用转换说明，用法和 printf 类似，但是用一点需要说明的是参数列表中的参数是变量的地址，scanf 会将读取的数据写入参数列表中提供的地址。</p><p>格式字符串中除了转换说明外都是“需要用户实际输入的字符”，scanf 函数会跳过格式字符串中转换说明以外的字符，在转换说明所在的位置读取用户输入的内容（空字符不是严格匹配，除了“%c”，其他转换说明都会忽略输入项前面的所有空白字符）。同时转换说明还为 scanf 函数表明了需要将用户输入的字符串转换成何种类型的数据，这样 scanf 函数就能用正确的格式编码输入的字符串，并将其存储到指定的相应类型变量中了。</p><p>scanf 函数的转换说明和 printf 函数基本相同，需要说明的一点是对于 double 类型的转换说明，printf 函数使用“%f”，而 scanf 使用“%lf”。</p><h4 id="2-读取用户输入的过程"><a href="#2-读取用户输入的过程" class="headerlink" title="2.读取用户输入的过程"></a>2.读取用户输入的过程</h4><p>按照《C Primer Plus》中的例子，我们假设 scanf 根据“%d”转换说明读取一个整数，从这里开始看它的读取过程。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-built_in">scanf</span>(<span class="hljs-string">&quot;%d&quot;</span>, &amp;a);<br></code></pre></td></tr></table></figure><p>用户在输入区输入了“  1A”（ 1 前面有三个空格）并按下了回车，按下回车的时候，系统将输入区用户输入的字符串一并发送给 scanf 函数，函数开始处理字符串，从字符串的开头开始，一次读取一个字符，跳过空白字符，即对用户输入的三个空格不予理会直接跳过。接着读取到了一个“1”字符，发现它是数字，继续读取下面一个字符“A”，发现“A”不是数字了。于是将 1 转换成整数的格式传入了 a 变量，并将无法处理的字符“A”放回到用户的输入区。</p><p>如果用户直接输入了“A”怎么办呢？</p><p>和上面的过程相似，但是当 scanf 读取到“A”的时候将“A”放回到用户输入区，无法给变量 a 赋值。若用户再次运行这句语句，并接着“A”输入了一个数字的话，scanf 还是不能读取到“A”后面的数字，因为被放回到输入区中的“A”再次被发送给 scanf 读取到并又放回了输入区中。如图：</p><p><img src="/img/scanf-scanf-input-process.jpg" alt="scanf 读取过程"></p><p>因为 scanf 会忽略用户输入的空白字符，所以我们可以用空白字符来分隔不同的数据，如：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-built_in">scanf</span>(<span class="hljs-string">&quot;%d%d&quot;</span>, &amp;num1, &amp;num2);<br></code></pre></td></tr></table></figure><p>输入“123  234”，空格会将 123 和 234 分成两个数字并分别读入 num1 和 num2 中，或者将空格换成回车也有同样的效果。</p><p>但是若使用“%c”，空白就不会不忽略了，如：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-built_in">scanf</span>(<span class="hljs-string">&quot;%d%c&quot;</span>, &amp;num, &amp;chara);<br></code></pre></td></tr></table></figure><p>若用户输入了“3&lt;回车&gt;”，那么 3 会被读入 num 中，换行符会被读入 chara 中；或者输入“3  A”，那么3会被读入 num 中，空格会被读入 chara 中，“A”会被 scanf 函数放回输入区。</p><h4 id="3-函数的返回值"><a href="#3-函数的返回值" class="headerlink" title="3.函数的返回值"></a>3.函数的返回值</h4><p>scanf 函数返回成功读取的项数，若没有成功读取则返回 0（如转换说明是一个“%d”，但用户却输入了一个字符，这就不算成功读取），当 scanf 读取到文件结尾时会返回 EOF。（使用 scanf 读取文件？）</p><p>通常我们可以使用 scanf 的返回值来判断用户是否进行了正确的输入，从而判断是否结束循环、处理不合理的输入或者进行其他操作等。如：</p><p><img src="/img/scanf-scanf-return-value.jpg" alt="使用函数返回值判断是否结束循环"></p><p>上图就是一个要求用户输入整数来循环的例子，用户可以通过输入一个字母等来退出循环。</p><h3 id="三、printf-和-scanf-的-修饰符"><a href="#三、printf-和-scanf-的-修饰符" class="headerlink" title="三、printf 和 scanf 的 * 修饰符"></a>三、printf 和 scanf 的 * 修饰符</h3><p>在 printf 函数中，“<em>”修饰符可以替代转换说明的修饰符，并用参数列表中相应位置的参数来替换“</em>”修饰符，从而可以使用程序中的变量来修改输出数据的格式。如：</p><p><img src="/img/scanf-printf-star.jpg" alt="printf 的 * 修饰符"></p><p>在上面的程序中， printf 中格式字符串里的第一个“<em>”会被 width 变量的值替换，第二个“</em>”会被 precision 的值替换，即变为“%8.2f”，再进行 data 的处理。</p><p>使用“*”修饰符可以让用户来决定数据的输出格式，更增加了程序的实用性和方便性。</p><p>以上。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;暑假在家看《C Primer Plus》，有一部分对 printf 和 scanf 函数讲得比较细，所以想要写一篇笔记记录一下这些知识。&lt;/p&gt;
&lt;h3 id=&quot;一、关于-printf-函数&quot;&gt;&lt;a href=&quot;#一、关于-printf-函数&quot; class=&quot;headerl</summary>
      
    
    
    
    <category term="Computer Science" scheme="https://maphical.cn/categories/Computer-Science/"/>
    
    <category term="Programming Language" scheme="https://maphical.cn/categories/Computer-Science/Programming-Language/"/>
    
    <category term="C" scheme="https://maphical.cn/categories/Computer-Science/Programming-Language/C/"/>
    
    
  </entry>
  
  <entry>
    <title>个人网站的插件和其执行</title>
    <link href="https://maphical.cn/2017/08/plugin-system-of-personal-website/"/>
    <id>https://maphical.cn/2017/08/plugin-system-of-personal-website/</id>
    <published>2017-08-24T12:15:47.000Z</published>
    <updated>2025-10-11T16:10:29.448Z</updated>
    
    <content type="html"><![CDATA[<p>开发网站的时候想要不登录服务器一条一条地执行命令来实现数据库、日志和网站文件的备份，那么就可以把这些操作编写成插件，并使用 GET 请求，通过 URL 传入的参数来配置和启动插件进行相应的操作。</p><p>这样做的好处有几点：</p><ul><li>不需要每次手动进行数据的备份工作，免去了那些登录服务器和对数据库执行命令的操作。</li><li>可以另外编写一些辅助的程序对此服务器进行更高级的操作，更容易地实现网站的运维自动化。</li><li>在发生紧急事件等需要立即保护数据安全、进行数据备份或者需要进行其他操作的情况下，可以大幅度提高操作的及时性和可靠性。</li></ul><p>具体实施方法是：</p><ul><li>在网站的根目录创建一个插件目录，将插件的“启动器（launcher）”放在目录中作为启动其他插件的控制器，并对传入的 GET 参数进行过滤防止危险的 URL 传入。我们可以在 launcher 中添加密码认证，URL 必须提供 launcher 中内置的密码来证明 URL 来自于可信的用户（可能这个认证有点简单了……），另外在 launcher 中对可以启动的插件做限制，只允许代码中规定的几个插件运行，其它不在这些规定的插件名称中的操作被全部舍弃。为了增加安全性，URL 传入的参数值只能被用作 switch 判断，不参与实际的代码运行。</li><li>在目录中（或者在网站目录以外更安全）创建一个插件库（lib）目录，将插件脚本文件放在 lib 目录中，可以在插件代码中做判断，若没有运行 launcher，那么插件脚本不能独自运行。</li></ul><p>注：</p><ul><li>可以把插件写成一个类的形式，若疏忽导致其独自运行，这样也不会产生很大的副作用。</li><li>在 launcher 中添加日志功能，并定期更换其中的密码。</li><li>学习更多知识后会更新这篇文章。</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;开发网站的时候想要不登录服务器一条一条地执行命令来实现数据库、日志和网站文件的备份，那么就可以把这些操作编写成插件，并使用 GET 请求，通过 URL 传入的参数来配置和启动插件进行相应的操作。&lt;/p&gt;
&lt;p&gt;这样做的好处有几点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不需要每次手动进</summary>
      
    
    
    
    <category term="Computer Science" scheme="https://maphical.cn/categories/Computer-Science/"/>
    
    <category term="Operation &amp; Maintenance" scheme="https://maphical.cn/categories/Computer-Science/Operation-Maintenance/"/>
    
    
  </entry>
  
  <entry>
    <title>腾讯云对象存储（COS）使用方法</title>
    <link href="https://maphical.cn/2017/08/how-to-use-tencent-cloud-cos/"/>
    <id>https://maphical.cn/2017/08/how-to-use-tencent-cloud-cos/</id>
    <published>2017-08-08T16:56:40.000Z</published>
    <updated>2025-10-12T14:54:52.367Z</updated>
    
    <content type="html"><![CDATA[<p>最近正在搭建自己的个人网站，使用了腾讯的云服务，找到了一个“<a href="/link/?t=41MXN9" target="_blank">对象存储（Cloud Object Storage）</a>”服务，就使用了一下试试看，这篇文章是一篇学习笔记。先介绍下这个服务，它的产品文档是这样说的：</p><blockquote><p>对象存储（Cloud Object Storage，COS）是由腾讯云推出的无目录层次结构、无数据格式限制，可容纳海量数据且支持 HTTP&#x2F;HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限，无需分区管理，适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。COS 提供网页端管理界面、多种主流开发语言的 SDK、API 以及命令行和图形化工具，并且兼容 S3 的 API 接口，方便用户直接使用社区工具和插件。</p><p>腾讯云官方网站：<a href="/link/?t=41MXN9" target="_blank">https://cloud.tencent.com/product/cos</a></p></blockquote><h3 id="1-创建存储桶（Bucket）"><a href="#1-创建存储桶（Bucket）" class="headerlink" title="1.创建存储桶（Bucket）"></a>1.创建存储桶（Bucket）</h3><p>打开自己的对象存储控制台- Bucket 列表，会看到一个如下图所示的“创建 Bucket”按钮。点击按钮创建 Bucket。Bucket 就是一个存储对象（文件）的空间，在存储路径中就是根目录（&#x2F;），我们可以在这个 Bucket 中创建目录并更改其中的内容。</p><p><img src="/img/cos-bucket.jpg" alt="创建 Bucket"></p><p>创建存储桶需要填写一些信息，如下图：</p><p><img src="/img/cos-input-bucket-info.jpg" alt="输入信息"></p><p>Bucket 名称只允许小写字母和数字的组合且不能超过 40 字符，地域选择的原则是靠近服务访问的集中区域，访问权限如图中所示，CDN 加速可以根据需要选择，如果开启的话上传的每个文件都会有一个自己的 CDN 加速 URL 和一个文件 URL。</p><p>创建 Bucket 后可以在 Bucket 列表的基础配置选项页面中配置 Bucket 的各种设置，对于个人网站的资源存储比较实用的是“防盗链设置”和“静态网站”两个选项了，但是如果开启了“私有读写”权限（在创建 Bucket 时设置）的话，黑白名单什么的开不开都可以吧。其中“静态网站”功能可以托管静态内容（HTML 页面或者包含客户端脚本的页面等）做成静态网站，并支持自定义域名访问。</p><h3 id="2-在自己的代码中访问"><a href="#2-在自己的代码中访问" class="headerlink" title="2.在自己的代码中访问"></a>2.在自己的代码中访问</h3><p>创建好存储用的 Bucket 后就可以使用了 COS 了，可以在“Bucket 列表”页面选中一个 Bucket 打开它的文件列表并上传文件、创建目录或者获取文件下载链接等。然而对于一个开发者来说，最重要的还是要能在代码中使用 COS 服务，这就要用到 COS 的 SDK（Software Development Kit，软件开发工具包） 了。COS 的 SDK 有很多的语言版本，详细在<a href="/link/?t=LNCO71" target="_blank">这里</a>。我的个人网站主要是使用 PHP 编写的，所以我就主要介绍一下 PHP 版本的 SDK 使用方法。</p><p>首先从 Github 上面下载 SDK 源码，网址在<a href="/link/?t=SYEUVF" target="_blank">这里</a>。源代码中有一个 include.php 文件，使用 SDK 提供的函数之前在文件中包含 include.php，使用 SDK 的命名空间并 new 出一个 cosApi 对象就可以使用对象中提供的方法了，代码如下：</p><p><img src="/img/cos-example-connection.jpg" alt="SDK 初始化"></p><p>解释一下图中的参数（并不是所有的参数都需要定义，主要取决于执行的操作是否需要那个参数，并且参数的名称也不是固定的，可以随意更改）：</p><ul><li>$bucket ——要操作的存储桶名；</li><li>$src ——所要操作的目标输入路径；</li><li>$dst(2) ——目标输出路径；</li><li>$folder —— COS 中的目录路径（创建新目录或其他操作时使用）；</li><li>$config ——放置用于连接 COS 所需信息的设置数组（关于 config 中的信息看<a href="/link/?t=71ZI4K" target="_blank">这里</a>）；</li><li>$cosApi ——存放 SDK 函数的对象（使用 $config 数组中的设置生成的）。</li></ul><p>创建 SDK 对象以后就可以使用对象中的方法对 COS 进行操作了，SDK 中不同方法可以实现不同的操作，如下（src 和 dst 参数若没有说明都是指在存储桶中的路径）：</p><ul><li><p><strong>$cosApi-&gt;createFolder($bucket, $folder, $bizAttr&#x3D;NULL)</strong></p><ul><li>创建新目录</li><li>bucket 参数是存储桶的名称（下同）</li><li>folder 参数是新目录的路径</li><li>bizAttr 参数是创建目录时为目录添加的附加自定义属性信息（输入字符串即可，可省略）</li></ul></li><li><p><strong>$cosApi-&gt;upload($bucket, $src, $dst)</strong></p><ul><li>上传文件</li><li>src 参数是要上传文件的路径</li><li>dst 参数是上传后的保存路径</li></ul></li><li><p><strong>$cosApi-&gt;download($bucket, $src, $dst)</strong></p><ul><li>下载文件</li><li>src 参数是要下载的文件路径</li><li>dst 参数是下载到的本地保存路径</li></ul></li><li><p><strong>$cosApi-&gt;listFolder($bucket, $folder)</strong></p><ul><li>列出目录中的内容</li><li>folder 参数是要列出内容的目标目录的路径</li></ul></li><li><p><strong>$cosApi-&gt;updateFolder($bucket, $folder, $bizAttr)</strong></p><ul><li>更新目录的自定义属性信息</li><li>folder 参数是要更新信息的目录的路径</li></ul></li><li><p><strong>$cosApi-&gt;update($bucket, $dst, $bizAttr, $authority, $customerHeaders)</strong></p><ul><li>更新文件信息</li><li>dst 参数是目标文件的路径</li><li>bizAttr 参数是自定义属性信息字符串</li><li>authority 参数是文件的读写权限（eInvalid，继承 Bucket 的读写权限；eWRPrivate，私有读写；eWPrivateRPublic，公有读私有写）</li><li>customerHeaders 参数是“用户自定义头域”数组，这个不是很了解也没有用过。</li></ul></li><li><p><strong>$cosApi-&gt;statFolder($bucket, $folder)</strong></p><ul><li>获取目录的状态信息</li><li>folder 参数是要获取状态信息的目录路径</li></ul></li><li><p><strong>$cosApi-&gt;stat($bucket, $dst)</strong></p><ul><li>获取文件的状态信息</li><li>dst 参数是要获取状态信息的文件的路径</li></ul></li><li><p><strong>$cosApi-&gt;delFile($bucket, $dst)</strong></p><ul><li>删除文件</li><li>dst 参数是要删除的文件的路径</li></ul></li><li><p><strong>$cosApi-&gt;delFolder($bucket, $folder)</strong></p><ul><li>删除目录</li><li>folder 参数是要删除的文件的路径</li></ul></li><li><p><strong>$cosApi-&gt;copyFile($bucket, $src, $dst)</strong></p><ul><li>复制文件</li><li>src 参数是要复制的文件的路径</li><li>dst 参数是复制到的路径</li></ul></li><li><p><strong>$cosApi-&gt;moveFile($bucket, $src, $dst)</strong></p><ul><li>移动文件</li><li>src 参数是要移动的文件的路径</li><li>dst 参数是要移动到的路径</li></ul></li></ul><p>那么怎么获取到以上函数所提供的信息呢？以上函数都有返回值，获取返回值可以得到自己想要的信息。返回值是数组形式的，内容一般有三个：</p><ol><li>code ——错误码，成功时为 0</li><li>message ——错误信息</li><li>data ——若请求数据了，则这里为数据</li></ol><h3 id="3-总结"><a href="#3-总结" class="headerlink" title="3.总结"></a>3.总结</h3><p>使用 COS 可以干什么呢？我觉得可以做个个人云盘啦，网站经常使用的资源存储啦，网站日志自动备份或者数据库备份什么的吧，再探索一下。以上就是我使用 COS 的经验，后续有了更多体会可能会继续更新这篇文章。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;最近正在搭建自己的个人网站，使用了腾讯的云服务，找到了一个“&lt;a href=&quot;/link/?t=41MXN9&quot; target=&quot;_blank&quot;&gt;对象存储（Cloud Object Storage）&lt;/a&gt;”服务，就使用了一下试试看，这篇文章是一篇学习笔记。先介绍下这个服务，</summary>
      
    
    
    
    <category term="Computer Science" scheme="https://maphical.cn/categories/Computer-Science/"/>
    
    <category term="Frameworks &amp; Tools" scheme="https://maphical.cn/categories/Computer-Science/Frameworks-Tools/"/>
    
    <category term="Tencent Cloud" scheme="https://maphical.cn/categories/Computer-Science/Frameworks-Tools/Tencent-Cloud/"/>
    
    
  </entry>
  
  <entry>
    <title>C语言的代码风格和注意事项</title>
    <link href="https://maphical.cn/2017/08/code-style-in-c/"/>
    <id>https://maphical.cn/2017/08/code-style-in-c/</id>
    <published>2017-08-06T13:09:24.000Z</published>
    <updated>2025-10-11T16:10:29.448Z</updated>
    
    <content type="html"><![CDATA[<p>这篇文章主要记录《C Primer Plus》中作者提醒学习者注意的 C 语言代码风格和注意事项等内容，是一篇学习笔记。</p><p><strong><span style="color: red">赋值或者计算时的数据类型转换</span></strong>—— C 语言中如 1.2 这样的浮点数都是默认为 double 类型的，1 这样的整数默认为 int 类型。当在为变量赋值的时候最好标明数值的类型（如：float a &#x3D; 1.2f;），或者使用强制类型转换确保计算或者赋值的时候数据类型正确（即使 C 语言会进行强制的数据类型升级或者降级，但还是建议使用人为的类型转换）。</p><p><strong><span style="color: red">查看不同数据类型的取值范围</span></strong>——在 limits.h 头文件中定义了一些符号常量标出当前系统中不同整数类型的取值范围，在 float.h 头文件中定义了当前系统中不同浮点数的取值范围，在使用数值时可以查看这些常量确保取值没有超出取值范围。</p><p><strong><span style="color: red">printf 函数和 scanf 函数的参数</span></strong>—— printf 和 scanf 函数的参数个数没有确定，它们的第一个参数——“格式字符串”中包含了后续参数的个数以及读取后续参数的格式。我们在编程时要确保格式转换说明符要与相应的变量数据类型相匹配。</p><p><strong><span style="color: red">sizeof 运算符运算对象的不同形式</span></strong>—— sizeof 的运算对象如果是数据类型关键字（如：int、float等），则要加括号将其括起来；如果是变量名则可以将其括起来或者不括。一般为了不出现错误，都写成加括号的形式即可。</p><p><strong><span style="color: red">写判断是否相等的关系表达式时，常量放在左边</span></strong>——这样写可以避免出现误用赋值运算符的情况，常量放在左边可以有助于编译器在误用赋值运算符时发现错误。</p><p><strong><span style="color: red">函数定义和使用常用步骤</span></strong>——1.通过原型声明函数；2.在程序中调用函数使用；3.定义函数。函数原型可以让编译器在调用函数时判断函数使用是否正确，关于为什么不先定义函数之后再使用，书中原文是：“现代的编程习惯是程序要素分为接口部分和实现部分……接口部分描述了如何使用一个特性，也就是函数原型所做的；实现部分描述了具体行为，这正是函数定义所做的”。</p><p><strong><span style="color: red">while 循环常用格式</span></strong>——</p><p><img src="/img/c-style-1.jpg" alt="while 循环格式"></p><p><strong><span style="color: red">while 循环常用格式 2</span></strong>——</p><p><img src="/img/c-style-2.jpg" alt="while 循环格式"></p><p><strong><span style="color: red">if else 形式1</span></strong>——</p><p><img src="/img/c-style-3.jpg" alt="if else 形式1"></p><p><strong><span style="color: red">if else 形式2</span></strong>——</p><p><img src="/img/c-style-4.jpg" alt="if else 形式2"></p><p><strong><span style="color: red">if else 形式3</span></strong>——</p><p><img src="/img/c-style-5.jpg" alt="if else 形式3"></p><p>以上。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;这篇文章主要记录《C Primer Plus》中作者提醒学习者注意的 C 语言代码风格和注意事项等内容，是一篇学习笔记。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;span style=&quot;color: red&quot;&gt;赋值或者计算时的数据类型转换&lt;/span&gt;&lt;/strong&gt;—— C 语</summary>
      
    
    
    
    <category term="Computer Science" scheme="https://maphical.cn/categories/Computer-Science/"/>
    
    <category term="Programming Language" scheme="https://maphical.cn/categories/Computer-Science/Programming-Language/"/>
    
    <category term="C" scheme="https://maphical.cn/categories/Computer-Science/Programming-Language/C/"/>
    
    
  </entry>
  
  <entry>
    <title>使用作为参数传入的数组指针计算数组大小</title>
    <link href="https://maphical.cn/2017/07/compute-size-of-array-in-c/"/>
    <id>https://maphical.cn/2017/07/compute-size-of-array-in-c/</id>
    <published>2017-07-23T05:49:05.000Z</published>
    <updated>2025-10-11T16:10:29.448Z</updated>
    
    <content type="html"><![CDATA[<p>在学校实验周编写程序时，需要将程序中的结构体数组存储到文件中。我编写了一个 StoreData 函数，实现接受结构体数组的首地址并将结构体数组在二进制模式下写入文件中的功能。并编写了一个 RequestData 函数用于从文件中读取出结构体数组。编写完成后进行测试，出现了以下类似的问题（不是实际代码，是为了讲解问题简单写的）：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">/*代码片段1*/</span><br><span class="hljs-type">int</span> <span class="hljs-title function_">ExampleFunction</span><span class="hljs-params">(<span class="hljs-built_in">array</span>)</span> <span class="hljs-comment">// array 是一个数组的首地址</span><br>&#123;<br>    <span class="hljs-type">size_t</span> sizeArray = <span class="hljs-keyword">sizeof</span>(<span class="hljs-built_in">array</span>);<br>    <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;Size of array is %u\n&quot;</span>, sizeArray);<br>    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>&#125;<br></code></pre></td></tr></table></figure><p>我在 StoreData 函数中使用上面类似的方法计算所接受数组的大小，array 表示数组首地址。</p><p>我用这种方法的原因是因为曾经在教科书上看到了下面类似的代码：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">/*代码片段2*/</span><br>...<span class="hljs-comment">/* main() */</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Student</span> <span class="hljs-title">a</span>[3] =</span> &#123;&#123;<span class="hljs-string">&quot;Lihong&quot;</span>, <span class="hljs-number">10001</span>, <span class="hljs-number">15</span>&#125;, &#123;<span class="hljs-string">&quot;Wanggang&quot;</span>, <span class="hljs-number">10002</span>, <span class="hljs-number">15</span>&#125;, &#123;<span class="hljs-string">&quot;Zhangqiang&quot;</span>, <span class="hljs-number">10003</span>, <span class="hljs-number">15</span>&#125;&#125;;<br>...<br><span class="hljs-type">size_t</span> studentSize = <span class="hljs-keyword">sizeof</span>(a);<br>...<br></code></pre></td></tr></table></figure><p>在上面的代码片段2中，studentSize 可以正确获得 a 的实际大小。</p><p>然而在代码片段1中，执行结果却是：</p><figure class="highlight smali"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs smali">Size of<span class="hljs-built_in"> array </span>is 4<br></code></pre></td></tr></table></figure><p>不能正确地计算数组的大小，导致存入文件中时数据不完整程序出错。</p><p>经过了解，原来，<strong>若在一个已经声明此指针为数组首地址的函数中，sizeof 是可以自动计算出整个数组的大小的。但是若离开了声明数组的作用域，比如上面指针作为参数传入另一个函数的时候，在这个函数内使用 sizeof 就只是计算了这个指针的大小，根据系统的不同会显示不同的大小。</strong></p><p>所以如果要在另一个函数中计算数组大小，可以把数组的长度传入函数，使用“长度*sizeof(元素)”来计算。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;在学校实验周编写程序时，需要将程序中的结构体数组存储到文件中。我编写了一个 StoreData 函数，实现接受结构体数组的首地址并将结构体数组在二进制模式下写入文件中的功能。并编写了一个 RequestData 函数用于从文件中读取出结构体数组。编写完成后进行测试，出现了以</summary>
      
    
    
    
    <category term="Computer Science" scheme="https://maphical.cn/categories/Computer-Science/"/>
    
    <category term="Programming Language" scheme="https://maphical.cn/categories/Computer-Science/Programming-Language/"/>
    
    <category term="C" scheme="https://maphical.cn/categories/Computer-Science/Programming-Language/C/"/>
    
    
  </entry>
  
  <entry>
    <title>C语言的数据类型</title>
    <link href="https://maphical.cn/2017/07/data-type-in-c/"/>
    <id>https://maphical.cn/2017/07/data-type-in-c/</id>
    <published>2017-07-21T10:37:21.000Z</published>
    <updated>2025-10-11T16:10:29.448Z</updated>
    
    <content type="html"><![CDATA[<p>最近暑假正在看《C Primer Plus》，借此机会写一些笔记发布在博客上，以后复习也比较容易。</p><p>C语言的基本数据类型由11个关键字组成：<strong>int, float, double, char, short, long, unsigned, signed, _Bool, _Imaginary, _Complex</strong>。</p><p><strong>这十一个关键字的不同组合可以生成不同的数据类型：</strong></p><h4 id="有符号或无符号（加-unsigned-关键字）整型："><a href="#有符号或无符号（加-unsigned-关键字）整型：" class="headerlink" title="有符号或无符号（加 unsigned 关键字）整型："></a><strong>有符号或无符号（加 unsigned 关键字）整型：</strong></h4><ul><li><strong>int</strong> —— 系统给定的基本整数类型，标准规定此类型存储空间不小于 16 位。</li><li><strong>short int &#x2F; short</strong> —— 该类型可表示的整数小于或等于最大的 int 类型整数，标准规定此类型存储空间至少为 16 位。</li><li><strong>long int &#x2F; long</strong> —— 该类型可表示的整数大于或等于最大的 int 类型，标准规定此类型存储空间至少占 32 位。</li><li><strong>long long int &#x2F; long long</strong> —— 该类型可表示的整数大于或等于最大的 long 类型，标准规定此类型存储空间至少为 64 位。</li></ul><h4 id="字符类型："><a href="#字符类型：" class="headerlink" title="字符类型："></a><strong>字符类型：</strong></h4><ul><li><strong>char</strong> —— 字符类型的关键字，此类型通常占 1 字节（8 位）的空间，但如果要表示基本字符集，也可以占用 16 位或更大的空间。在 C 语言中，字符类型其实存储的都是整数，之所以可以显示字符，是因为系统将以 char 类型存储的整数显示为了 ASCII 表或其他编码表对应的字符。所以 char 类型也可以用于存储或处理较小的整数类型，也可以使用 unsigned 关键字修饰 char 关键字（在一些系统中使用有符号的整数存储字符类型，有些系统使用无符号的，可以使用 signed 或 unsigned 关键字明确令系统使用某一种形式）。</li></ul><h4 id="布尔类型："><a href="#布尔类型：" class="headerlink" title="布尔类型："></a><strong>布尔类型：</strong></h4><ul><li><strong>_Bool</strong> —— 布尔类型是 C99 标准新添加的数据类型，用于表示 true 或 false。因为在 C 中使用 1 表示 true，0 表示 false，所以此类型只需要 1 位（0 或 1）的存储空间。</li></ul><h4 id="实数浮点类型："><a href="#实数浮点类型：" class="headerlink" title="实数浮点类型："></a><strong>实数浮点类型：</strong></h4><ul><li><strong>float</strong> —— 系统基本的浮点数据类型，可精确表示至少 6 有效数字。</li><li><strong>double</strong> —— 相比于 float 类型，此类型可以存储更大范围和更高精度的数值（更多有效数字，至少 10 位，通常会更多，和更大的指数）。</li><li><strong>long double</strong> —— 存储数值范围比 double 更大，原因同上。</li></ul><h4 id="复数和虚数浮点数："><a href="#复数和虚数浮点数：" class="headerlink" title="复数和虚数浮点数："></a><strong>复数和虚数浮点数：</strong></h4><ul><li><strong>float _imaginary</strong> —— 带有虚数单位的 float 类型。</li><li><strong>double _Imaginary</strong> —— 带有虚数单位的 double 类型。</li><li><strong>long double _Imaginary</strong> —— 带有虚数单位的 long double 类型。</li><li><strong>float _Complex</strong> —— 由 float 类型组成实部和虚部的复数。</li><li><strong>double _Complex</strong> —— 由 double 类型组成实部和虚部的复数。</li><li><strong>long double _Complex</strong> —— 由 long double 类型组成实部和虚部的复数。</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;最近暑假正在看《C Primer Plus》，借此机会写一些笔记发布在博客上，以后复习也比较容易。&lt;/p&gt;
&lt;p&gt;C语言的基本数据类型由11个关键字组成：&lt;strong&gt;int, float, double, char, short, long, unsigned, sig</summary>
      
    
    
    
    <category term="Computer Science" scheme="https://maphical.cn/categories/Computer-Science/"/>
    
    <category term="Programming Language" scheme="https://maphical.cn/categories/Computer-Science/Programming-Language/"/>
    
    <category term="C" scheme="https://maphical.cn/categories/Computer-Science/Programming-Language/C/"/>
    
    
  </entry>
  
</feed>
