<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>梦的天空之城</title>
  
  
  <link href="/atom.xml" rel="self"/>
  
  <link href="https://hexo.xinsane.com/"/>
  <updated>2018-12-29T04:03:44.592Z</updated>
  <id>https://hexo.xinsane.com/</id>
  
  <author>
    <name>xinsane</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>编译linux-4.19内核并添加自定义文件系统</title>
    <link href="https://hexo.xinsane.com/2018/12/23/%E7%BC%96%E8%AF%91linux-4.19%E5%86%85%E6%A0%B8%E5%B9%B6%E6%B7%BB%E5%8A%A0%E8%87%AA%E5%AE%9A%E4%B9%89%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F/"/>
    <id>https://hexo.xinsane.com/2018/12/23/编译linux-4.19内核并添加自定义文件系统/</id>
    <published>2018-12-23T10:31:35.000Z</published>
    <updated>2018-12-29T04:03:44.592Z</updated>
    
    <content type="html"><![CDATA[<h2 id="实验目的"><a href="#实验目的" class="headerlink" title="实验目的"></a>实验目的</h2><ul><li>设计一个类似 ext2 的自定义文件系统 myext2</li><li>添加自定义文件系统到 linux 内核中</li><li>测试自定义文件系统</li></ul><h2 id="实验过程"><a href="#实验过程" class="headerlink" title="实验过程"></a>实验过程</h2><p>本次实验使用 <a href="https://github.com/torvalds/linux/tree/v4.19" target="_blank" rel="noopener">linux-4.19</a> 内核版本，基于<code>ext2</code>文件系统，除了文件系统的<code>magic number</code>不同之外基本没有修改，只是为了了解和测试基本操作。</p><h3 id="准备编译环境"><a href="#准备编译环境" class="headerlink" title="准备编译环境"></a>准备编译环境</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo apt install git fakeroot build-essential ncurses-dev xz-utils libssl-dev bc flex libelf-dev bison</span><br></pre></td></tr></table></figure><h3 id="下载源码"><a href="#下载源码" class="headerlink" title="下载源码"></a>下载源码</h3><p>可以从 <a href="https://github.com/torvalds/linux/releases" target="_blank" rel="noopener">Github</a> 上下载源码，这里我选取了当前的最新稳定版 <a href="https://github.com/torvalds/linux/releases/tag/v4.19" target="_blank" rel="noopener">v4.19</a> 作为测试版本。下载后解压到工作目录，源码就准备完毕了。</p><p>注：由于该项目的<code>commit</code>特别多，不建议以<code>git clone</code>的方式获取代码，因为这会花费相当多的时间。</p><h3 id="修改代码添加文件系统"><a href="#修改代码添加文件系统" class="headerlink" title="修改代码添加文件系统"></a>修改代码添加文件系统</h3><h4 id="fs-myext2目录的修改"><a href="#fs-myext2目录的修改" class="headerlink" title="fs/myext2目录的修改"></a>fs/myext2目录的修改</h4><p>以<code>ext2</code>文件系统为蓝本，修改成自定义的<code>myext2</code>文件系统，基本修改如下所示：<br><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 复制ext2文件系统到myext2作为自定义文件系统的蓝本</span></span><br><span class="line">cp -r fs/ext2 fs/myext2</span><br><span class="line"></span><br><span class="line"><span class="built_in">cd</span> fs/myext2</span><br><span class="line"></span><br><span class="line"><span class="comment"># 替换文件名（大小写都需要替换）</span></span><br><span class="line">rename <span class="string">"s/ext2/myext2/"</span> *</span><br><span class="line">rename <span class="string">"s/EXT2/MYEXT2/"</span> *</span><br><span class="line"></span><br><span class="line"><span class="comment"># 替换文件内容（大小写都需要替换）</span></span><br><span class="line">sed -i <span class="string">"s/ext2/myext2/g"</span> `grep ext2 -rl ./`</span><br><span class="line">sed -i <span class="string">"s/EXT2/MYEXT2/g"</span> `grep EXT2 -rl ./`</span><br></pre></td></tr></table></figure></p><p>需要特别注意，现在的myext2文件系统中是没有定义<code>myext2_set_bit_atomic</code>和<code>myext2_clear_bit_atomic</code>这两个函数的，参照ext4的做法，简单的定义一个预编译宏即可。<br><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// file: fs/myext2/myext2.h</span></span><br><span class="line">line <span class="number">20</span>: <span class="meta">#<span class="meta-keyword">define</span> myext2_set_bit_atomic        ext2_set_bit_atomic</span></span><br><span class="line">line <span class="number">21</span>: <span class="meta">#<span class="meta-keyword">define</span> myext2_clear_bit_atomic      ext2_clear_bit_atomic</span></span><br></pre></td></tr></table></figure></p><h4 id="fs目录的修改"><a href="#fs目录的修改" class="headerlink" title="fs目录的修改"></a>fs目录的修改</h4><p>除了对myext2目录文件的修改，还需要对<code>fs/Kconfig</code>和<code>fs/Makefile</code>作相应的修改，修改的基本原则是，只要出现了<code>ext2</code>的配置，就复制一份到<code>myext2</code>的配置（大写的<code>EXT2</code>也是如此）。<br><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 可以像这样查找需要处理的内容，也可以直接打开文件查找，需要修改的内容并不多</span></span><br><span class="line">cat fs/Kconfig | grep -ni ext2</span><br><span class="line">cat fs/Makefile | grep -ni ext2</span><br><span class="line"></span><br><span class="line"><span class="comment"># 修改后的内容部分如下所示</span></span><br><span class="line"><span class="comment"># Kconfig</span></span><br><span class="line"><span class="comment"># 引入子目录下的Kconfig</span></span><br><span class="line"><span class="built_in">source</span> <span class="string">"fs/ext2/Kconfig"</span></span><br><span class="line"><span class="built_in">source</span> <span class="string">"fs/myext2/Kconfig"</span></span><br><span class="line"></span><br><span class="line">config FS_MBCACHE</span><br><span class="line"><span class="comment"># Meta block cache for Extended Attributes (ext2/ext3/ext4)</span></span><br><span class="line"><span class="comment"># 这一段代码用于指定下一步配置中FS_MBCACHE的默认值</span></span><br><span class="line">        tristate</span><br><span class="line">        default y <span class="keyword">if</span> EXT2_FS=y &amp;&amp; EXT2_FS_XATTR</span><br><span class="line">        default y <span class="keyword">if</span> MYEXT2_FS=y &amp;&amp; MYEXT2_FS_XATTR</span><br><span class="line">        default y <span class="keyword">if</span> EXT4_FS=y</span><br><span class="line">        default m <span class="keyword">if</span> EXT2_FS_XATTR || EXT4_FS</span><br><span class="line">        default m <span class="keyword">if</span> MYEXT2_FS_XATTR || EXT4_FS</span><br><span class="line"></span><br><span class="line"><span class="comment"># Makefile</span></span><br><span class="line"><span class="comment"># 编译子目录</span></span><br><span class="line">obj-$(CONFIG_EXT2_FS)           += ext2/</span><br><span class="line">obj-$(CONFIG_MYEXT2_FS)         += myext2/</span><br></pre></td></tr></table></figure></p><h4 id="include目录的修改"><a href="#include目录的修改" class="headerlink" title="include目录的修改"></a>include目录的修改</h4><p>添加<code>myext2_fs.h</code>包含头文件：<br><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><span class="line"><span class="comment"># 添加myext2文件系统的基础支持，添加后需要像之前一样替换掉所有大小写的ext2</span></span><br><span class="line">cp include/linux/ext2_fs.h include/linux/myext2_fs.h</span><br><span class="line">sed -i <span class="string">"s/ext2/myext2/g"</span> include/linux/myext2_fs.h</span><br><span class="line">sed -i <span class="string">"s/EXT2/MYEXT2/g"</span> include/linux/myext2_fs.h</span><br></pre></td></tr></table></figure></p><p>修改自定义文件系统的<code>magic number</code>：<br><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// file: include/uapi/linux/magic.h</span></span><br><span class="line">line <span class="number">22</span>: <span class="meta">#<span class="meta-keyword">define</span> EXT2_SUPER_MAGIC        0xEF53</span></span><br><span class="line">line <span class="number">23</span>: <span class="meta">#<span class="meta-keyword">define</span> MYEXT2_SUPER_MAGIC      0x6666</span></span><br></pre></td></tr></table></figure></p><h3 id="基本配置与编译安装"><a href="#基本配置与编译安装" class="headerlink" title="基本配置与编译安装"></a>基本配置与编译安装</h3><p>在这一步中，需要配置编译好的内核需要打包哪些内容和模块，在<code>menuconfig</code>中，带<code>*</code>号的表示打包到二进制内核文件中，带<code>M</code>标识的表示以模块的形式加载。</p><p>在Kconfig文件中指定了一些默认值，即使全新配置也不需要从零开始。但是从当前系统拷贝一份配置再进行修改比全新配置更适合本机系统，相对全新配置不容易出错一点。在配置时应特别注意文件系统子配置项中是否配置了新增文件系统的模块。</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 拷贝当前系统内核配置到工作目录，也可以不执行这一步而进行配置</span></span><br><span class="line">cp /boot/config-$(uname -r) .config</span><br><span class="line"></span><br><span class="line"><span class="comment"># 图形化配置</span></span><br><span class="line">make menuconfig</span><br><span class="line"></span><br><span class="line"><span class="comment"># 编译内核和各模块</span></span><br><span class="line">make</span><br><span class="line"></span><br><span class="line"><span class="comment"># 安装模块（默认安装位置为/lib/modules）</span></span><br><span class="line">make modules_install</span><br><span class="line"></span><br><span class="line"><span class="comment"># 安装内核并更新grub（默认安装位置为/boot）</span></span><br><span class="line">make install</span><br><span class="line"></span><br><span class="line"><span class="comment"># 重启系统</span></span><br><span class="line">sudo reboot</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看内核是否更新成功</span></span><br><span class="line">uname -r <span class="comment"># 查看内核版本号</span></span><br><span class="line">uname -a <span class="comment"># 查看内核完整版本信息</span></span><br></pre></td></tr></table></figure><p>在<code>make menuconfig</code>完成之后，编译时出现了如下错误：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">No rule to make target &apos;debian/certs/benh@debian.org.cert.pem&apos;, needed by &apos;certs/x509_certificate_list&apos;.</span><br></pre></td></tr></table></figure></p><p>这里投机取巧直接注释掉<code>CONFIG_SYSTEM_TRUSTED_KEYS</code>就可以成功编译了。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">// file: .config</span><br><span class="line">line 8205: # CONFIG_SYSTEM_TRUSTED_KEYS=&quot;&quot;</span><br></pre></td></tr></table></figure></p><h3 id="设计格式化工具"><a href="#设计格式化工具" class="headerlink" title="设计格式化工具"></a>设计格式化工具</h3><p>内核安装成功后，并不能直接使用<code>mkfs -t myext2 /dev/{target}</code>，因为编译的内核中并不包含格式化工具<code>mkfs.myext2</code><br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mkfs: failed to execute mkfs.myext2: No such file or directory</span><br></pre></td></tr></table></figure></p><p>其实，myext2文件系统与ext2在物理布局上除了magic number完全一致，所以替代方案就是使用<code>mkfs.ext2</code>代替然后修改<code>magic number</code>。修改<code>magic number</code>可以直接使用<code>hexedit</code>搜索<code>53EF</code>（<code>0xEF53</code>的存储方式是低字节在前）修改成<code>6666</code>。为了方便修改，写了一段简单的C语言程序：<br><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">char</span> *argv[])</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">if</span>(argc &lt; <span class="number">2</span>)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"usage: %s %s\n"</span>, argv[<span class="number">0</span>], <span class="string">"filename"</span>);</span><br><span class="line"><span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">&#125;</span><br><span class="line">FILE* file = fopen(argv[<span class="number">1</span>], <span class="string">"r+b"</span>);</span><br><span class="line"><span class="keyword">if</span> (file == <span class="literal">NULL</span>)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"can not to open file: %s\n"</span>, argv[<span class="number">1</span>]);</span><br><span class="line"><span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">&#125;</span><br><span class="line">fseek(file, <span class="number">0x438</span>, SEEK_SET);</span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">short</span> magic = <span class="number">0</span>;</span><br><span class="line">fread(&amp;magic, <span class="number">2</span>, <span class="number">1</span>, file);</span><br><span class="line"><span class="keyword">if</span> (<span class="number">0xef53</span> == magic)</span><br><span class="line">&#123;</span><br><span class="line">fseek(file, <span class="number">0x438</span>, SEEK_SET);</span><br><span class="line">magic = <span class="number">0x6666</span>;</span><br><span class="line">fwrite(&amp;magic, <span class="number">2</span>, <span class="number">1</span>, file);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"magic number changed 0x%x =&gt; 0x%x\n"</span>, <span class="number">0xef53</span>, magic);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"magic number = 0x%x.\nnot a ext2 format. magic number not changed.\n"</span>, magic);</span><br><span class="line">fclose(file);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h3 id="测试自定义文件系统"><a href="#测试自定义文件系统" class="headerlink" title="测试自定义文件系统"></a>测试自定义文件系统</h3><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></pre></td><td class="code"><pre><span class="line">dd <span class="keyword">if</span>=/dev/zero of=./vfs bs=1M count=1</span><br><span class="line">mkfs.ext2 ./vfs</span><br><span class="line">./chmn ./vfs</span><br><span class="line"><span class="comment"># 输出</span></span><br><span class="line">magic number changed 0xef53 =&gt; 0x6666</span><br></pre></td></tr></table></figure><p>分别建立两个目录，同时挂载刚创建的虚拟文件系统，可以正常的读写文件，且对任意挂载目录进行修改都会反映到其他挂载目录，部分操作和输出如下所示：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">root@kali:~/workspace# mkdir vfs_mount</span><br><span class="line">root@kali:~/workspace# mkdir vfs_mount_clone</span><br><span class="line">root@kali:~/workspace# mount -t myext2 ./vfs ./vfs_mount -o loop</span><br><span class="line">root@kali:~/workspace# mount -t myext2 ./vfs ./vfs_mount_clone -o loop</span><br><span class="line">root@kali:~/workspace# ls vfs_mount</span><br><span class="line">lost+found</span><br><span class="line">root@kali:~/workspace# mkdir vfs_mount/test</span><br><span class="line">root@kali:~/workspace# echo 123 &gt; vfs_mount_clone/test.txt</span><br><span class="line">root@kali:~/workspace# ls vfs_mount</span><br><span class="line">lost+found  test  test.txt</span><br><span class="line">root@kali:~/workspace# cat vfs_mount/test.txt</span><br><span class="line">123</span><br><span class="line">root@kali:~/workspace# ls vfs_mount_clone</span><br><span class="line">lost+found  test  test.txt</span><br></pre></td></tr></table></figure></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;实验目的&quot;&gt;&lt;a href=&quot;#实验目的&quot; class=&quot;headerlink&quot; title=&quot;实验目的&quot;&gt;&lt;/a&gt;实验目的&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;设计一个类似 ext2 的自定义文件系统 myext2&lt;/li&gt;
&lt;li&gt;添加自定义文件系统到 linux 内核
      
    
    </summary>
    
      <category term="Linux" scheme="https://hexo.xinsane.com/categories/Linux/"/>
    
    
      <category term="Linux" scheme="https://hexo.xinsane.com/tags/Linux/"/>
    
  </entry>
  
  <entry>
    <title>Apache如何rewrite到子目录</title>
    <link href="https://hexo.xinsane.com/2018/09/13/Apache%E5%A6%82%E4%BD%95rewrite%E5%88%B0%E5%AD%90%E7%9B%AE%E5%BD%95/"/>
    <id>https://hexo.xinsane.com/2018/09/13/Apache如何rewrite到子目录/</id>
    <published>2018-09-13T08:25:39.000Z</published>
    <updated>2018-09-13T08:39:15.583Z</updated>
    
    <content type="html"><![CDATA[<p>今天遇到一个非常紧急的问题，看起来也很常见，就是网站的一个子模块不是直接提供服务的，而是在public子目录下提供网页，然而不能把public带进URL里面，就是说我的URL应该是这样的<code>/submodule/index.html</code>，文件地址应该是这样的<code>submodule/public/index.html</code>。</p><p>然而网站结构前期已经定好，不能直接把public子目录配置为网站的根目录（这个模块本身就在网站根目录的子目录下面），所以需要在这个模块的目录下配置重写规则rewrite到public子目录。于是乎，我想当然的配置了如下.htaccess：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&lt;IfModule mod_rewrite.c&gt;</span><br><span class="line">  Options +FollowSymlinks -Multiviews</span><br><span class="line">  RewriteEngine On</span><br><span class="line"></span><br><span class="line">  RewriteRule ^(.*)$ public/$1</span><br><span class="line">&lt;/IfModule&gt;</span><br></pre></td></tr></table></figure><p>然而Apache却报了500错误，查日志，Rewrite次数过多。什么意思呢？就是在第一次进入子模块目录时，加上了一个public之后，会从头再来一次，再一次进入这个目录，继续重写，无限循环，就爆炸了。怎么办？实测在Rule后面加L标记是无效的。</p><p>Google搜索：Apache如何rewrite到子目录，无果。我就纳闷了，这个需求应该很常见吧，怎么就搜不到呢？再搜英文，依然无果。难道是我的打开方式不对？</p><p>折腾了很久，最后终于发现一个神器（所谓神奇就是不知道为什么就可以了）：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">&lt;IfModule mod_rewrite.c&gt;</span><br><span class="line">  Options +FollowSymlinks -Multiviews</span><br><span class="line">  RewriteEngine On</span><br><span class="line"></span><br><span class="line">  RewriteRule ^public(.*)$ - [L]</span><br><span class="line">  RewriteRule ^(.*)$ public/$1</span><br><span class="line">&lt;/IfModule&gt;</span><br></pre></td></tr></table></figure><p>两条Rule依次执行，当发现当前URL已经有了public，直接中断查询，于是就不会溢出了（中间的-意思是只匹配不替换），大概可能是这样？最后我试了一下，子目录再子目录也是可以的，只要再复制一份.htaccess到子目录就行了。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;今天遇到一个非常紧急的问题，看起来也很常见，就是网站的一个子模块不是直接提供服务的，而是在public子目录下提供网页，然而不能把public带进URL里面，就是说我的URL应该是这样的&lt;code&gt;/submodule/index.html&lt;/code&gt;，文件地址应该是这样
      
    
    </summary>
    
      <category term="Apache" scheme="https://hexo.xinsane.com/categories/Apache/"/>
    
    
      <category term="Apache" scheme="https://hexo.xinsane.com/tags/Apache/"/>
    
  </entry>
  
  <entry>
    <title>Ubuntu16.04交叉编译OpenWrt版本scutclient</title>
    <link href="https://hexo.xinsane.com/2018/07/01/Ubuntu16-04%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%91OpenWrt%E7%89%88%E6%9C%ACscutclient/"/>
    <id>https://hexo.xinsane.com/2018/07/01/Ubuntu16-04交叉编译OpenWrt版本scutclient/</id>
    <published>2018-06-30T18:25:50.000Z</published>
    <updated>2018-06-30T18:33:09.004Z</updated>
    
    <content type="html"><![CDATA[<p>按照<a href="https://github.com/scutclient/scutclient/blob/master/README.md" target="_blank" rel="noopener">官方文档</a>的说法一步步来，需要openwrt sdk和scutclient的Makefile文件即可。在编译的过程主要遇到以下两个问题：</p><h4 id="1-awk"><a href="#1-awk" class="headerlink" title="1. awk"></a>1. awk</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">……</span><br><span class="line">awk: include/scan.awk: line 21: function asort never defined</span><br><span class="line">awk: include/scan.awk: line 21: function asort never defined</span><br><span class="line">awk: include/scan.awk: line 21: function asort never defined</span><br><span class="line">awk: include/scan.awk: line 21: function asort never defined</span><br><span class="line">……</span><br></pre></td></tr></table></figure><p>这是因为没有安装gawk<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo apt install gawk</span><br></pre></td></tr></table></figure></p><h4 id="2-ccache"><a href="#2-ccache" class="headerlink" title="2. ccache"></a>2. ccache</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">……</span><br><span class="line">configure: error: C compiler cannot create executables</span><br><span class="line">……</span><br></pre></td></tr></table></figure><p>这是因为没有安装ccache<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo apt install ccache</span><br></pre></td></tr></table></figure></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;按照&lt;a href=&quot;https://github.com/scutclient/scutclient/blob/master/README.md&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;官方文档&lt;/a&gt;的说法一步步来，需要openwrt sdk和s
      
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title>RecyclerView探幽 - Adapter、ViewHolder、ItemList</title>
    <link href="https://hexo.xinsane.com/2018/05/12/RecyclerView%E6%8E%A2%E5%B9%BD%20-%20Adapter%E3%80%81ViewHolder%E3%80%81ItemList/"/>
    <id>https://hexo.xinsane.com/2018/05/12/RecyclerView探幽 - Adapter、ViewHolder、ItemList/</id>
    <published>2018-05-12T07:21:22.000Z</published>
    <updated>2018-06-30T18:34:59.637Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Adapter中的onCreateViewHolder和onBindViewHolder"><a href="#Adapter中的onCreateViewHolder和onBindViewHolder" class="headerlink" title="Adapter中的onCreateViewHolder和onBindViewHolder"></a>Adapter中的onCreateViewHolder和onBindViewHolder</h2><p>在实现Adapter的时候，至少需要重写三个方法：getItemCount、onCreateViewHolder和onBindViewHolder，getItemCount一般是返回数据表的长度，比较简单，但是onCreateViewHolder和onBindViewHolder中遇到的坑比较多。</p><h3 id="ViewHolder-onCreateViewHolder-ViewGroup-parent-int-viewType"><a href="#ViewHolder-onCreateViewHolder-ViewGroup-parent-int-viewType" class="headerlink" title="ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)"></a>ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)</h3><p>这个方法中会构造两个东西，一个叫View，很熟悉，另一个叫ViewHolder，虽然不那么熟悉，但是顾名思义ViewHolder就是用来装View的容器。可以把View中的一些控件提取出来放到ViewHolder中存起来，避免每次都findViewById，也可以把一些与View相关的临时变量放在里面，最重要的一点就是<strong>ViewHolder与View永久绑定</strong>，所以把View对象的引用存在ViewHolder中可以随时从ViewHolder中获取对应的View来操作视图。Javadoc是这样写的：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">Called when RecyclerView needs a new ViewHolder of the given type to represent</span><br><span class="line">an item.</span><br><span class="line"></span><br><span class="line">当RecyclerView需要一个对应类型的新的ViewHolder时会调用这个方法。</span><br><span class="line"></span><br><span class="line">This new ViewHolder should be constructed with a new View that can represent the</span><br><span class="line">items of the given type. You can either create a new View manually or inflate it</span><br><span class="line">from an XML layout file.</span><br><span class="line"></span><br><span class="line">你应当用一个对应类型的view来初始化一个ViewHolder，你可以自己构建这个view，也可以通过XML</span><br><span class="line">布局文件中构建。</span><br><span class="line"></span><br><span class="line">The new ViewHolder will be used to display items of the adapter using</span><br><span class="line">onBindViewHolder(ViewHolder, int, List). Since it will be re-used to display</span><br><span class="line">different items in the data set, it is a good idea to cache references to sub</span><br><span class="line">views of the View to avoid unnecessary View.findViewById(int) calls.</span><br><span class="line"></span><br><span class="line">这个新的ViewHolder将会在onBindViewHolder中绑定数据并显示在屏幕上。因为它会在更换数据的</span><br><span class="line">时候重复利用，所以在ViewHolder中保存子视图的引用来减少findViewById的调用是个不错的做法。</span><br></pre></td></tr></table></figure></p><p>这里有个坑，<strong>这里创建的ViewHolder和与之对应的View永久绑定，但是它们跟数据只是临时绑定！</strong><br>什么意思呢，绝对不可以把数据放在ViewHolder里面，因为ViewHolder和Data的关系会变。举个例子，ViewHolder相当于抽屉，在抽屉里放东西（Data），一个抽屉里面的东西可以是你有的任何一件，东西的数量可以远大于抽屉数量，因为多的可以不放在抽屉里，但是ViewHolder的数量就那么多，它不会随着东西的增多而变多。</p><h3 id="void-onBindViewHolder-ViewHolder-holder-int-position"><a href="#void-onBindViewHolder-ViewHolder-holder-int-position" class="headerlink" title="void onBindViewHolder(ViewHolder holder, int position)"></a>void onBindViewHolder(ViewHolder holder, int position)</h3><p>这个方法在数据与View-ViewHolder绑定的时候被调用的，看Javadoc：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">Called by RecyclerView to display the data at the specified position. This method should</span><br><span class="line">update the contents of the ViewHolder.itemView to reflect the item at the given position.</span><br><span class="line"></span><br><span class="line">RecyclerView在特定位置显示数据的时候会调用这个方法，这个方法应当根据对应位置的数据来更新ViewHolder</span><br><span class="line">中对应的View视图。</span><br><span class="line"></span><br><span class="line">Note that unlike ListView, RecyclerView will not call this method again if the position </span><br><span class="line">of the item changes in the data set unless the item itself is invalidated or the new</span><br><span class="line">position cannot be determined. For this reason, you should only use the position parameter</span><br><span class="line">while acquiring the related data item inside this method and should not keep a copy of it.</span><br><span class="line">If you need the position of an item later on (e.g. in a click listener), use</span><br><span class="line">ViewHolder.getAdapterPosition() which will have the updated adapter position.</span><br><span class="line"></span><br><span class="line">注意它将不同于ListView，在完成绑定之后，当对应位置的数据被修改之后，这个方法不会被重新调用，除非</span><br><span class="line">item本身已经失效或者新的位置无法确定。（在数据改变时，开发者应当主动地调用notify*系列方法通知刷新</span><br><span class="line">视图，之后RecyclerView会重新调用这个方法重新绑定视图）。因为这个原因，你应该用position参数从</span><br><span class="line">DataList中获取数据而不能把数据存在ViewHolder里面。如果在之后需要获取position，可以用</span><br><span class="line">ViewHolder.getAdapterPosition()来实时获取当时绑定的数据position。</span><br><span class="line"></span><br><span class="line">Override onBindViewHolder(ViewHolder, int, List) instead if Adapter can handle efficient</span><br><span class="line">partial bind.</span><br></pre></td></tr></table></figure></p><h2 id="多种Item类型的应用"><a href="#多种Item类型的应用" class="headerlink" title="多种Item类型的应用"></a>多种Item类型的应用</h2><p>待更新…</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;Adapter中的onCreateViewHolder和onBindViewHolder&quot;&gt;&lt;a href=&quot;#Adapter中的onCreateViewHolder和onBindViewHolder&quot; class=&quot;headerlink&quot; title=&quot;Adap
      
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title>树莓派的那些事</title>
    <link href="https://hexo.xinsane.com/2018/04/25/%E6%A0%91%E8%8E%93%E6%B4%BE%E7%9A%84%E9%82%A3%E4%BA%9B%E4%BA%8B/"/>
    <id>https://hexo.xinsane.com/2018/04/25/树莓派的那些事/</id>
    <published>2018-04-25T09:28:37.000Z</published>
    <updated>2018-04-25T17:05:45.018Z</updated>
    
    <content type="html"><![CDATA[<h2 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h2><p>树莓派入手很久了，一直搁置在那也没怎么用，最近突发奇想就拿来重新刷了系统，遇到了很多问题，故简单地记录一下。</p><h2 id="安装系统"><a href="#安装系统" class="headerlink" title="安装系统"></a>安装系统</h2><p>本次选择了官方系统的Lite版，基于Debian，我使用的版本是2018年4月18日更新的，其他版本其实应该都大同小异，但据了解Lite版和Desktop版差别挺大的。<a href="https://www.raspberrypi.org/downloads/raspbian/" target="_blank" rel="noopener">还是给个传送门吧</a>。刷入系统我选择的是官方推荐的ETCher，界面十分简约，功能也十分简约，就是专门用来干这个的，当然其他的刷入工具也是可以的。</p><h2 id="开启ssh"><a href="#开启ssh" class="headerlink" title="开启ssh"></a>开启ssh</h2><p>其实这个系统默认就是有安装好ssh，只是默认没有开启而已。<br>启动ssh<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo /etc/init.d/ssh start</span><br></pre></td></tr></table></figure></p><p>或者配置开机自启后再启动<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">sudo systemctl enable ssh</span><br><span class="line">sudo systemctl start ssh</span><br></pre></td></tr></table></figure></p><p>然后就可以用电脑通过ssh访问了<br>顺便记录一下，官方系统默认帐号是pi，默认密码是raspberry<br>ssh连接之前记得查看一下树莓派的ip地址<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ifconfig</span><br></pre></td></tr></table></figure></p><p>我用路由器给我的树莓派绑定了固定ip地址，或者可以在树莓派里配置静态ip，具体方法不再赘述。</p><h3 id="禁止密码登陆ssh"><a href="#禁止密码登陆ssh" class="headerlink" title="禁止密码登陆ssh"></a>禁止密码登陆ssh</h3><p>首先修改默认用户密码，以免被别人趁虚而入。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">passwd</span><br></pre></td></tr></table></figure></p><p>然后建立.ssh目录上传公钥<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">cd ~</span><br><span class="line">mkdir .ssh</span><br><span class="line">cd .ssh</span><br><span class="line">nano authorized_keys</span><br></pre></td></tr></table></figure></p><p>然后粘贴公钥，<strong>Ctrl+O</strong> 保存，<strong>Ctrl+X</strong> 退出nano（使用其他编辑器比如vi也可以，我习惯用nano，后面的编辑都会用这个编辑器），然后退出shell尝试使用密钥登陆，如果能登陆成功，就可以禁止密码登陆了<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo nano /etc/ssh/sshd_config</span><br></pre></td></tr></table></figure></p><p>找到PasswordAuthentication这一项<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">#PasswordAuthentication yes</span><br></pre></td></tr></table></figure></p><p>取消注释并改成no<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">PasswordAuthentication no</span><br></pre></td></tr></table></figure></p><p>然后保存退出，重启ssh服务<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo systemctl restart ssh</span><br></pre></td></tr></table></figure></p><p>然后退出shell，再登陆时就只能用密钥登陆了</p><h2 id="换源-or-代理？"><a href="#换源-or-代理？" class="headerlink" title="换源 or 代理？"></a>换源 or 代理？</h2><p>树莓派的官方源在境外，国内访问会比较慢，网上的说法比较倾向于换源，这也确实是一个很不错的方法。但是在实际的使用中，一定要确认使用的源是否正确。我开始换了阿里的源，速度确实非常快，但是很多包安装不了，大部分报情况下显示，可能请求了一个不稳定版的软件并且缺少一些依赖，这种情况下无法自动安装依赖（正常情况下安装一个软件缺少依赖时会自动安装），网上可以通过列出所有的依赖然后依次安装的方法解决这个问题，但是我发现只要换回了官方源就不会有这个问题。同时国内的源跟官方源不匹配也是一个问题，有时候国内会更新的慢很多，所以在换源的时候一定要小心了。最后我还是决定使用代理的方式来解决速度慢的问题（理论上使用代理网络请求，请求同样是要出境，但经过实际测试使用代理就是会比正常访问快很多，我也没有具体研究原因）。</p><h3 id="使用-shadowsocks-libev-privoxy-实现自由命令行代理"><a href="#使用-shadowsocks-libev-privoxy-实现自由命令行代理" class="headerlink" title="使用 shadowsocks-libev + privoxy 实现自由命令行代理"></a>使用 shadowsocks-libev + privoxy 实现自由命令行代理</h3><h4 id="安装-shadowsocks-libev"><a href="#安装-shadowsocks-libev" class="headerlink" title="安装 shadowsocks-libev"></a>安装 shadowsocks-libev</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo apt install shadowsocks-libev</span><br></pre></td></tr></table></figure><p>配置文件在 <strong>/etc/shadowsocks-libev/config.json</strong>，填写好服务器的配置</p><h4 id="配置登陆时自动启动"><a href="#配置登陆时自动启动" class="headerlink" title="配置登陆时自动启动"></a>配置登陆时自动启动</h4><p>在 <strong>/etc/rc.local</strong> 后面 <strong>exit 0</strong> 前面添加一行：（会把日志文件输出到pi用户的用户根目录的ss.log）<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">su pi -c &quot;nohup ss-local &gt; /home/pi/ss.log 2&gt;&amp;1 &amp;&quot;</span><br></pre></td></tr></table></figure></p><h4 id="安装-privoxy"><a href="#安装-privoxy" class="headerlink" title="安装 privoxy"></a>安装 privoxy</h4><p>现在虽然shadowsocks已经能够自动启动了，但是ss-local使用的是socks5代理，http协议是不能用它来代理的，所以需要一个名为privoxy的软件：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo apt install privoxy</span><br></pre></td></tr></table></figure></p><h4 id="配置-privoxy-全局代理"><a href="#配置-privoxy-全局代理" class="headerlink" title="配置 privoxy 全局代理"></a>配置 privoxy 全局代理</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo nano /etc/privoxy/whitelist.action</span><br></pre></td></tr></table></figure><p>写入以下内容：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">&#123;&#123;alias&#125;&#125;</span><br><span class="line"># 代理(socks5)</span><br><span class="line">socks5 = +forward-override&#123;forward-socks5 127.0.0.1:1080 .&#125;</span><br><span class="line"># 直连</span><br><span class="line">direct = +forward-override&#123;forward .&#125;</span><br><span class="line"></span><br><span class="line"># 所有网站走代理</span><br><span class="line">&#123;socks5&#125;</span><br><span class="line">/</span><br><span class="line"># 以下网站走直连</span><br><span class="line">&#123;direct&#125;</span><br><span class="line">.ip.cn</span><br><span class="line">.chinaz.com</span><br></pre></td></tr></table></figure></p><p><strong>其中端口号要与ss-local一致，这里我使用了1080端口</strong><br>然后把 <strong>actionsfile whitelist.action</strong> 添加到 <strong>/etc/privoxy/config</strong> 的末尾就完成了配置<br>最后重启privoxy并设置开机自启，看一下是否启动成功：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">sudo systemctl enable privoxy</span><br><span class="line">sudo systemctl start privoxy</span><br><span class="line">sudo systemctl status privoxy</span><br></pre></td></tr></table></figure></p><p>目前，只要配置了 <strong>http_proxy</strong> 和 <strong>https_proxy</strong> 环境变量就能使用代理了，但是一旦配置了这个环境变量，所有的请求都会代理，如果希望选择性的代理，可以使用 gfwlist 实现 pac 模式，关于这点，我并没有过多的了解。</p><h4 id="编写-proxy-脚本实现自由代理"><a href="#编写-proxy-脚本实现自由代理" class="headerlink" title="编写 proxy 脚本实现自由代理"></a>编写 proxy 脚本实现自由代理</h4><p>所谓自由代理就是希望代理的时候通过在命令前加上 proxy 关键字进行代理访问，不需要代理的请求不加这个关键字就行了，无需其他配置。建议把脚本放在 <strong>/usr/local/bin/proxy</strong><br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo nano /usr/local/bin/proxy</span><br></pre></td></tr></table></figure></p><p>写入以下内容：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">#!/bin/bash</span><br><span class="line">http_proxy=http://127.0.0.1:8118 https_proxy=http://127.0.0.1:8118 $*</span><br></pre></td></tr></table></figure></p><p><strong>其中端口号要与 privoxy 的配置一致，默认是8118</strong><br>然后赋予可执行权限：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo chmod +x /usr/local/bin/proxy</span><br></pre></td></tr></table></figure></p><p>至此，所有配置完成，重启后，需要代理的命令使用proxy前缀，比如：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo proxy apt install git</span><br></pre></td></tr></table></figure></p><h2 id="修改默认交换文件的大小"><a href="#修改默认交换文件的大小" class="headerlink" title="修改默认交换文件的大小"></a>修改默认交换文件的大小</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo nano /etc/dphys-swapfile</span><br></pre></td></tr></table></figure><p>修改其中的<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">CONF_SWAPSIZE=100</span><br></pre></td></tr></table></figure></p><p>为<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">CONF_SWAPSIZE=2048</span><br></pre></td></tr></table></figure></p><p>就有2G的虚拟内存，应该够用了，最后执行：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">sudo dphys-swapfile setup</span><br><span class="line">sudo dphys-swapfile swapon</span><br></pre></td></tr></table></figure></p><p>重新加载一下配置文件，然后就可以用 free 命令查看到 2G 虚拟内存了</p><h2 id="让-shell-界面有颜色"><a href="#让-shell-界面有颜色" class="headerlink" title="让 shell 界面有颜色"></a>让 shell 界面有颜色</h2><p>pi用户的shell界面默认是有颜色的，但是root用户默认没有，只要把pi用户的.bashrc文件中对应的部分复制到root用户的.bashrc中就可以达到目的<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line">case &quot;$TERM&quot; in</span><br><span class="line">    xterm-color|*-256color) color_prompt=yes;;</span><br><span class="line">esac</span><br><span class="line"></span><br><span class="line">force_color_prompt=yes</span><br><span class="line"></span><br><span class="line">if [ -n &quot;$force_color_prompt&quot; ]; then</span><br><span class="line">    if [ -x /usr/bin/tput ] &amp;&amp; tput setaf 1 &gt;&amp;/dev/null; then</span><br><span class="line">color_prompt=yes</span><br><span class="line">    else</span><br><span class="line">color_prompt=</span><br><span class="line">    fi</span><br><span class="line">fi</span><br><span class="line"></span><br><span class="line">if [ &quot;$color_prompt&quot; = yes ]; then</span><br><span class="line">    PS1=&apos;$&#123;debian_chroot:+($debian_chroot)&#125;\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w \$\[\033[00m\] &apos;</span><br><span class="line">else</span><br><span class="line">    PS1=&apos;$&#123;debian_chroot:+($debian_chroot)&#125;\u@\h:\w\$ &apos;</span><br><span class="line">fi</span><br><span class="line">unset color_prompt force_color_prompt</span><br><span class="line"></span><br><span class="line">case &quot;$TERM&quot; in</span><br><span class="line">xterm*|rxvt*)</span><br><span class="line">    PS1=&quot;\[\e]0;$&#123;debian_chroot:+($debian_chroot)&#125;\u@\h: \w\a\]$PS1&quot;</span><br><span class="line">    ;;</span><br><span class="line">*)</span><br><span class="line">    ;;</span><br><span class="line">esac</span><br><span class="line"></span><br><span class="line">if [ -x /usr/bin/dircolors ]; then</span><br><span class="line">    test -r ~/.dircolors &amp;&amp; eval &quot;$(dircolors -b ~/.dircolors)&quot; || eval &quot;$(dircolors -b)&quot;</span><br><span class="line">    alias ls=&apos;ls --color=auto&apos;</span><br><span class="line">    alias grep=&apos;grep --color=auto&apos;</span><br><span class="line">    alias fgrep=&apos;fgrep --color=auto&apos;</span><br><span class="line">    alias egrep=&apos;egrep --color=auto&apos;</span><br><span class="line">fi</span><br><span class="line"></span><br><span class="line">alias ll=&apos;ls -al&apos;</span><br></pre></td></tr></table></figure></p><p>最后一句 alias 是把 ll 变成 ls -al 的别名，方便查看文件</p><h2 id="安装-apache2-php7-mysql-phpmyadmin"><a href="#安装-apache2-php7-mysql-phpmyadmin" class="headerlink" title="安装 apache2 + php7 + mysql + phpmyadmin"></a>安装 apache2 + php7 + mysql + phpmyadmin</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">sudo apt install apache2</span><br><span class="line">sudo apt install mysql-server mysql-client</span><br><span class="line">sudo apt install php php-mysql</span><br></pre></td></tr></table></figure><h3 id="mysql创建最高权限用户"><a href="#mysql创建最高权限用户" class="headerlink" title="mysql创建最高权限用户"></a>mysql创建最高权限用户</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">GRANT ALL PRIVILEGES ON *.* TO admin@localhost IDENTIFIED BY &apos;password&apos; WITH GRANT OPTION;</span><br></pre></td></tr></table></figure><h3 id="安装phpmyadmin"><a href="#安装phpmyadmin" class="headerlink" title="安装phpmyadmin"></a>安装phpmyadmin</h3><p>以phpMyAdmin-4.8.0.1为例（我修改了apache2的默认配置，网站根目录在 <strong>/var/www</strong>）<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">sudo mkdir /opt/phpmyadmin</span><br><span class="line">cd /opt/phpmyadmin</span><br><span class="line">sudo curl -O https://files.phpmyadmin.net/phpMyAdmin/4.8.0.1/phpMyAdmin-4.8.0.1-all-languages.zip</span><br><span class="line">sudo unzip phpMyAdmin-4.8.0.1-all-languages.zip</span><br><span class="line">sudo ln -s /opt/phpmyadmin/phpMyAdmin-4.8.0.1-all-languages /var/www/phpmyadmin</span><br><span class="line">sudo cp /var/www/phpmyadmin/config.sample.inc.php /var/www/phpmyadmin/config.inc.php</span><br><span class="line">sudo mkdir /var/www/phpmyadmin/tmp</span><br><span class="line">sudo chown www-data -R /var/www/phpmyadmin/tmp</span><br><span class="line">sudo chgrp www-data -R /var/www/phpmyadmin/tmp</span><br></pre></td></tr></table></figure></p><h3 id="安装tomcat"><a href="#安装tomcat" class="headerlink" title="安装tomcat"></a>安装tomcat</h3><h4 id="创建tomcat用户和组"><a href="#创建tomcat用户和组" class="headerlink" title="创建tomcat用户和组"></a>创建tomcat用户和组</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">sudo groupadd tomcat</span><br><span class="line">sudo useradd  -s  /bin/false  -g  tomcat  -d  /opt/tomcat tomcat</span><br></pre></td></tr></table></figure><h4 id="以9-0-7版本为例下载源码并配置"><a href="#以9-0-7版本为例下载源码并配置" class="headerlink" title="以9.0.7版本为例下载源码并配置"></a>以9.0.7版本为例下载源码并配置</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">sudo mkdir /opt/tomcat</span><br><span class="line">cd /opt/tomcat</span><br><span class="line">sudo curl -O http://mirrors.shu.edu.cn/apache/tomcat/tomcat-9/v9.0.7/bin/apache-tomcat-9.0.7.zip</span><br><span class="line">sudo unzip apache-tomcat-9.0.7.zip</span><br><span class="line">sudo mv -R apache-tomcat-9.0.7 9.0.7</span><br><span class="line">sudo ln -s /opt/tomcat/9.0.7 /opt/tomcat/latest</span><br><span class="line"># 修改权限</span><br><span class="line">sudo chown tomcat -R 9.0.7</span><br><span class="line">sudo chgrp tomcat -R 9.0.7</span><br><span class="line">sudo chmod 744 -R /opt/tomcat/latest/bin/*.sh</span><br></pre></td></tr></table></figure><h4 id="配置开机启动"><a href="#配置开机启动" class="headerlink" title="配置开机启动"></a>配置开机启动</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo update-java-alternatives  -l</span><br></pre></td></tr></table></figure><p>记下jdk路径（RASPBIAN系统+openjdk的sdk位置默认是 <strong>/usr/lib/jvm/java-1.8.0-openjdk-armhf</strong>）<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo nano /etc/systemd/system/tomcat.service</span><br></pre></td></tr></table></figure></p><p>写入以下内容<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">[Unit]</span><br><span class="line">Description=Apache Tomcat Web Application Container</span><br><span class="line">After=network.target</span><br><span class="line"></span><br><span class="line">[Service]</span><br><span class="line">Type=forking</span><br><span class="line">Environment=JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-armhf/jre</span><br><span class="line">Environment=CATALINA_PID=/opt/tomcat/latest/temp/tomcat.pid</span><br><span class="line">Environment=CATALINA_HOME=/opt/tomcat/latest</span><br><span class="line">Environment=CATALINA_BASE=/opt/tomcat/latest</span><br><span class="line">Environment=&apos;CATALINA_OPTS=-Xms512M -Xmx1024M -server -XX:+UseParallelGC&apos;</span><br><span class="line">Environment=&apos;JAVA_OPTS=-Djava.awt.headless=true -Djava.security.egd=file:/dev/./urandom&apos;</span><br><span class="line">ExecStart=/opt/tomcat/latest/bin/startup.sh</span><br><span class="line">ExecStop=/opt/tomcat/latest/bin/shutdown.sh</span><br><span class="line">User=tomcat</span><br><span class="line">Group=tomcat</span><br><span class="line"></span><br><span class="line">[Install]</span><br><span class="line">WantedBy=multi-user.target</span><br></pre></td></tr></table></figure></p><p>注意替换jdk位置，以及其后的jre不能遗漏，内存配置按需修改<br>重新加载systemctl、配置开机启动、启动tomcat以及查看是否成功启动：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">sudo systemctl daemon-reload</span><br><span class="line">sudo systemctl enable tomcat</span><br><span class="line">sudo systemctl start tomcat</span><br><span class="line">sudo systemctl status tomcat</span><br></pre></td></tr></table></figure></p><h4 id="配置tomcat-web管理接口"><a href="#配置tomcat-web管理接口" class="headerlink" title="配置tomcat web管理接口"></a>配置tomcat web管理接口</h4><p>修改：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo nano /opt/tomcat/latest/conf/tomcat-users.xml</span><br></pre></td></tr></table></figure></p><p>在tomcat-users中添加一行<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;user username=&quot;tomcat&quot; password=&quot;password&quot; roles=&quot;manager-gui,admin-gui&quot;/&gt;</span><br></pre></td></tr></table></figure></p><p>修改META-INF权限配置，需要修改以下两个文件，允许局域网访问<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">sudo nano /opt/tomcat/latest/webapps/manager/META-INF/context.xml</span><br><span class="line">sudo nano /opt/tomcat/latest/webapps/host-manager/META-INF/context.xml</span><br></pre></td></tr></table></figure></p><p>两个文件中<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&lt;Context antiResourceLocking=&quot;false&quot; privileged=&quot;true&quot; &gt;</span><br><span class="line">  &lt;Valve className=&quot;org.apache.catalina.valves.RemoteAddrValve&quot;</span><br><span class="line">         allow=&quot;127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1&quot; /&gt;</span><br><span class="line">  &lt;Manager sessionAttributeValueClassNameFilter=&quot;java\.lang\.(?:Boolean|Integer|Long|Number|String)|org\.apache\.catalina\.filters\.CsrfPreventionFilter\$LruCache(?:\$1)?|java\.util\.(?:Linked)?HashMap&quot;/&gt;</span><br><span class="line">&lt;/Context&gt;</span><br></pre></td></tr></table></figure></p><p>都需要把<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">allow=&quot;127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1&quot;</span><br></pre></td></tr></table></figure></p><p>修改为<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">allow=&quot;127\.\d+\.\d+\.\d+|192\.168\.\d+\.\d+|::1|0:0:0:0:0:0:0:1&quot;</span><br></pre></td></tr></table></figure></p><p>即允许来自192.168.开头的IP的访问，这样就可以通过局域网管理web应用了<br>最终文件长这个样子：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&lt;Context antiResourceLocking=&quot;false&quot; privileged=&quot;true&quot; &gt;</span><br><span class="line">  &lt;Valve className=&quot;org.apache.catalina.valves.RemoteAddrValve&quot;</span><br><span class="line">         allow=&quot;127\.\d+\.\d+\.\d+|192\.168\.\d+\.\d+|::1|0:0:0:0:0:0:0:1&quot; /&gt;</span><br><span class="line">  &lt;Manager sessionAttributeValueClassNameFilter=&quot;java\.lang\.(?:Boolean|Integer|Long|Number|String)|org\.apache\.catalina\.filters\.CsrfPreventionFilter\$LruCache(?:\$1)?|java\.util\.(?:Linked)?HashMap&quot;/&gt;</span><br><span class="line">&lt;/Context&gt;</span><br></pre></td></tr></table></figure></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;引言&quot;&gt;&lt;a href=&quot;#引言&quot; class=&quot;headerlink&quot; title=&quot;引言&quot;&gt;&lt;/a&gt;引言&lt;/h2&gt;&lt;p&gt;树莓派入手很久了，一直搁置在那也没怎么用，最近突发奇想就拿来重新刷了系统，遇到了很多问题，故简单地记录一下。&lt;/p&gt;
&lt;h2 id=&quot;安装系
      
    
    </summary>
    
      <category term="树莓派" scheme="https://hexo.xinsane.com/categories/%E6%A0%91%E8%8E%93%E6%B4%BE/"/>
    
    
      <category term="树莓派" scheme="https://hexo.xinsane.com/tags/%E6%A0%91%E8%8E%93%E6%B4%BE/"/>
    
      <category term="Debian" scheme="https://hexo.xinsane.com/tags/Debian/"/>
    
  </entry>
  
  <entry>
    <title>SpringMVC学习心得</title>
    <link href="https://hexo.xinsane.com/2018/04/21/SpringMVC%E5%AD%A6%E4%B9%A0%E5%BF%83%E5%BE%97/"/>
    <id>https://hexo.xinsane.com/2018/04/21/SpringMVC学习心得/</id>
    <published>2018-04-21T07:36:31.000Z</published>
    <updated>2018-04-21T09:19:53.138Z</updated>
    
    <content type="html"><![CDATA[<h1 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h1><p>在这里记录一下在初学SpringMVC中遇到的一些问题或编码习惯。<strong>强调一下，这主要是一些个人习惯，不是教程，更不适合初学者看</strong>。现在的新开始的网站基本都会前后分离，Web后台的开发重心基本都转向了API开发，所以我就在这方面死磕了2333</p><h1 id="Controller类注解"><a href="#Controller类注解" class="headerlink" title="Controller类注解"></a>Controller类注解</h1><figure class="highlight java"><figcaption><span>API控制器类的注解</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Controller</span></span><br><span class="line"><span class="meta">@RequestMapping</span>(value = <span class="string">"/api/main/sub"</span>,</span><br><span class="line">        produces = &#123; <span class="string">"application/json;charset=UTF-8"</span> &#125;,</span><br><span class="line">        method = RequestMethod.POST)</span><br><span class="line"><span class="meta">@ResponseBody</span></span><br></pre></td></tr></table></figure><h2 id="Controller"><a href="#Controller" class="headerlink" title="@Controller"></a>@Controller</h2><p>用来说明这是一个控制器，需要在SpringMVC的配置文件中指定自动扫描<br>指定base-package之后，该包及下面的所有子孙包都会被自动扫描<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">context:component-scan</span> <span class="attr">base-package</span>=<span class="string">"com.xinsane.controller"</span>/&gt;</span></span><br></pre></td></tr></table></figure></p><h2 id="RequestMapping-value"><a href="#RequestMapping-value" class="headerlink" title="@RequestMapping.value"></a>@RequestMapping.value</h2><p>指定该控制器的基本路径，我一般习惯以api开头，这样完整路径为：/api/{模块}/{控制器}/{方法}，没错，我可能ThinkPHP学傻了，但是不得不承认，这种设计方式在很多时候还是很不错的</p><h2 id="RequestMapping-produces"><a href="#RequestMapping-produces" class="headerlink" title="@RequestMapping.produces"></a>@RequestMapping.produces</h2><p>这个属性是用来指定返回数据的格式的，开发API的时候一般是返回JSON，所以这里指定为”application/json;charset=UTF-8”</p><h2 id="RequestMapping-method"><a href="#RequestMapping-method" class="headerlink" title="@RequestMapping.method"></a>@RequestMapping.method</h2><p>我习惯于用POST来请求所有API，所以这里设置成了POST</p><h1 id="RequestBody的使用和请求数据接收器"><a href="#RequestBody的使用和请求数据接收器" class="headerlink" title="@RequestBody的使用和请求数据接收器"></a>@RequestBody的使用和请求数据接收器</h1><p>@RequestBody会自动解析请求体的Body内容到指定的变量，这里是一个例子<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> String <span class="title">login</span><span class="params">(@RequestBody RequestData data)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (data.code == <span class="keyword">null</span> || data.code.isEmpty())</span><br><span class="line">        <span class="keyword">return</span> error(<span class="number">101</span>, <span class="string">"缺少参数"</span>);</span><br><span class="line">    TokenTransfer transfer = userService.loginByWxCode(data.code);</span><br><span class="line">    <span class="keyword">if</span> (transfer.getError() != <span class="number">0</span>)</span><br><span class="line">        <span class="keyword">return</span> error(transfer.getError(), transfer.getMsg());</span><br><span class="line">    JsonObject obj = <span class="keyword">new</span> JsonObject();</span><br><span class="line">    obj.addProperty(<span class="string">"error"</span>, <span class="number">0</span>);</span><br><span class="line">    obj.addProperty(<span class="string">"token"</span>, transfer.getToken());</span><br><span class="line">    <span class="keyword">return</span> obj.toString();</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">RequestData</span> <span class="keyword">extends</span> <span class="title">User</span> </span>&#123;</span><br><span class="line">    String code; <span class="comment">// 小程序传来的登陆code</span></span><br><span class="line">    String token; <span class="comment">// 小程序传来的用户token，用于识别用户</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setCode</span><span class="params">(String code)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.code = code;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setToken</span><span class="params">(String token)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.token = token;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>在这段代码中，User是一个pojo实体类，我基于它派生了一个请求数据类，并给它加上@RequestBody注解，前端传来的数据会解析到这个类中。</p><p>一般在一个控制器中，像新增数据、修改数据时一般都会用到对应的实体类中的字段，同时还会有其他字段，比如识别用户的token，小程序登陆是要用的code等，所以基于对应实体类派生一个专用的数据接收器还是挺妥的。这样做还有一个好处是，因为是基于pojo派生的，可以直接传递这个派生类到Service层，然后Service用基类pojo类接收（为什么不直接接收派生类？因为这样会破坏不同层之间的独立性，不便于维护，详见下一节）。</p><p>值得一提的是，在同一个控制器中，虽然有很多方法，但不必为每个方法单独派生数据接收器，实体类中有的字段可以直接用，没有的字段全部写入派生类中，SpringMVC不会因为一个你有的属性前端没传而产生不良后果。</p><p><strong>这里我使用了一个名词：数据接收器，这不是专业说法，仅仅是为了叙述方便引入的</strong></p><h1 id="Controller层与Service层之间的数据传输"><a href="#Controller层与Service层之间的数据传输" class="headerlink" title="Controller层与Service层之间的数据传输"></a>Controller层与Service层之间的数据传输</h1><p><strong>注：这里只讨论通过方法的参数列表和返回值来传递数据</strong></p><p>在传输数据方面，我习惯遵循一个原则：<strong>尽可能降低不同层之间的耦合程度</strong>。怎么说呢，举个例子，上一节创建的数据接收器是一个控制器的内部类，虽然只要声明为public也是可以在Service层引用的，但是一旦这么做了，对应的Controller和Service的耦合程度就会大大提高，设想如果另一个控制器想要调用同一个Service方法，还得找一个不相关的Controller下面的内部类，喵喵喵？？？</p><h2 id="Controller-gt-Service"><a href="#Controller-gt-Service" class="headerlink" title="Controller -&gt; Service"></a>Controller -&gt; Service</h2><p>从Controller传数据到Service其实很容易，因为Controller调用的Service的方法，需要多少参数直接写在参数列表就好了。这里有一个Service方法设计的基本原则：<strong>方法参数不宜过长</strong>。很容易理解，如果Service提供一个有十几个甚至几十个参数的方法，你能记住参数的含义和顺序吗？网上大多推荐用一个包装类，但其实大多数情况下只需要一个对应实体类和其他的少数参数就足够了。上一节基于实体类派生了一个数据接收器来接收前端数据，在这里正好用上，把数据接收器直接传入Service，在Service用基类接收，然后其他参数以参数列表传入。<strong>如果遇到除了实体类还有一大堆参数的，就要考虑提取一个公用包装器了</strong>，这种情况我暂时还没遇到，不再赘述。</p><h2 id="Service-gt-Controller"><a href="#Service-gt-Controller" class="headerlink" title="Service -&gt; Controller"></a>Service -&gt; Controller</h2><p>但是从Service返回数据就不那么方便了，如果Service要返回的东西很简单，简单到可以用一个基本类型或公用实体来表示的话，那没什么可说的，直接返回就好了。但如果要返回很复杂的东西，就不好办了，一方面Java中方法只能返回一个值，另一方面又不能让不同层之间耦合太过于严重。其实说难也不难解决，前面Controller传数据给Service时，不能传递自己类下面的内部类给Service，是因为一个Service可能会被多个Controller调用，但细细来考虑Controller和Service之间的关系，会发现总是由Service来提供服务，然后Controller来调用，我们也是一直这样做的，不可能出现Service调用Controller的情况。既然如此，在Controller使用Service的服务时，返回一个对应Service的专用返回数据包装器是合理的，“你要使用我提供的服务，就得遵守我的规则”。</p><h2 id="引入Transfer"><a href="#引入Transfer" class="headerlink" title="引入Transfer"></a>引入Transfer</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 基本接口，大多数情况下Controller只需要知道Service执行的有没有错误，有的话错误代号和错误信息是什么</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Transfer</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">getError</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="function">String <span class="title">getMsg</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 基本实现</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BaseTransfer</span> <span class="keyword">implements</span> <span class="title">Transfer</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> error = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">private</span> String msg;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getError</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> error;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> BaseTransfer <span class="title">setError</span><span class="params">(<span class="keyword">int</span> error)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.error = error;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getMsg</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> msg;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> BaseTransfer <span class="title">setMsg</span><span class="params">(String msg)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.msg = msg;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 如果还需要更多的信息，就需要自定义一个类来实现基本接口了</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TokenTransfer</span> <span class="keyword">implements</span> <span class="title">Transfer</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> error = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">private</span> String token;</span><br><span class="line">    <span class="keyword">private</span> String msg;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getError</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> error;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> TokenTransfer <span class="title">setError</span><span class="params">(<span class="keyword">int</span> error)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.error = error;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getToken</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> token;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> TokenTransfer <span class="title">setToken</span><span class="params">(String token)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.token = token;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getMsg</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> msg;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> TokenTransfer <span class="title">setMsg</span><span class="params">(String msg)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.msg = msg;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>至此，Controller与Service两层之间的数据传输问题算是解决了。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;引言&quot;&gt;&lt;a href=&quot;#引言&quot; class=&quot;headerlink&quot; title=&quot;引言&quot;&gt;&lt;/a&gt;引言&lt;/h1&gt;&lt;p&gt;在这里记录一下在初学SpringMVC中遇到的一些问题或编码习惯。&lt;strong&gt;强调一下，这主要是一些个人习惯，不是教程，更不适合初学者看
      
    
    </summary>
    
      <category term="Java" scheme="https://hexo.xinsane.com/categories/Java/"/>
    
      <category term="SSM" scheme="https://hexo.xinsane.com/categories/Java/SSM/"/>
    
      <category term="SpringMVC" scheme="https://hexo.xinsane.com/categories/Java/SSM/SpringMVC/"/>
    
    
      <category term="Java" scheme="https://hexo.xinsane.com/tags/Java/"/>
    
      <category term="SSM" scheme="https://hexo.xinsane.com/tags/SSM/"/>
    
      <category term="SpringMVC" scheme="https://hexo.xinsane.com/tags/SpringMVC/"/>
    
  </entry>
  
  <entry>
    <title>Spring+SpringMVC+Mybatis学习之路</title>
    <link href="https://hexo.xinsane.com/2018/04/21/Spring+SpringMVC+Mybatis%E5%AD%A6%E4%B9%A0%E4%B9%8B%E8%B7%AF/"/>
    <id>https://hexo.xinsane.com/2018/04/21/Spring+SpringMVC+Mybatis学习之路/</id>
    <published>2018-04-21T06:51:40.000Z</published>
    <updated>2018-04-21T07:35:58.764Z</updated>
    
    <content type="html"><![CDATA[<p><strong><center>这篇文章仅做学习过程的记录，不涉及技术问题</center></strong></p><h2 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h2><p>前段时间确定了走JavaWeb方向，所以在了解过Servlet和JSP之后便义无反顾的投入到了SSM三大框架的学习之中。在学习的过程中碰到了很多问题，而且很多很杂很乱，于是想写点什么来记录一下。这是我的第一篇技术博客，算是一次艰难的尝试吧2333</p><h2 id="入门"><a href="#入门" class="headerlink" title="入门"></a>入门</h2><p>三大框架我是从视频开始入手学的。其实以前我比较反对看视频学习，因为<strong>视频学习的周期比较长，而且很容易形成一种跟视频中类似的思维定式，视频里不规范、做的不好的地方很难察觉，自己在潜移默化中就跟着形成了同样的问题</strong>。这一次之所以选择看视频是因为实在是没有合适的学习资源，以前我一般通过看书来学习新东西比较多，但是这三大框架的更新速度实在过快，英文版图书没有查过，但是合适的中文书实在是没找到，基本都是很久远年代的版本，虽然网上说大体架构差不多，但我还是觉得这样会遇到太多问题。于是最终选择了前两年发布的一套视频教程来作为入门学习。</p><h2 id="实践"><a href="#实践" class="headerlink" title="实践"></a>实践</h2><p>在看视频的同时，我一边跟着视频写一些小测试，一边策划着用新学的框架写点什么。在把视频看完之后，便迫不及待的开始用它们来做一个当前的网站后台任务。看视频的时候觉得好像是这么回事，但是真正自己写的时候就完全不是那么回事了，各种问题，各种冗余，各种不爽，哎呀好气啊，怎么会这样呢？？？</p><p>心平气和真的很重要，当我终于静下心来的时候，已经浪费了很多时间了，我才意识到，学习得有一个过程，不能一蹴而就。我也想过一上来就拿新学的东西做复杂的项目是不是有点不妥，但是我习惯于在实践中学习，一些很枯燥的东西，实践一下就能心中有数，所以我并没有放弃，那就只能一点一点的排查错误，<strong>最终找到了我已开始忽略但是十分十分重要的东西，那就是官方文档。</strong>为什么一开始不从官方文档看起？一方面是因为我英文不好，看着很吃力，但更重要的是我对这几个框架一点都不熟悉，甚至都不知道它们是用来干嘛的，这种情况下，看官方文档只会一头雾水。当我从视频中了解了它们的基本知识和架构了之后，官方文档就显得十分重要了。<strong>一个框架往往是十分庞大的，选择其中的一小部分便可以完成任务，所以跟着别人的脚步只能学到他当时用到的东西</strong>，而且框架更新也可能很快，这个时候<strong>看官方文档能对框架的一些特性进行比较全面的了解</strong>，不用每个特性都去实践，但是看过之后心中会有数，很多时候茫然不是不知道该怎么做，而是不知道能做什么。</p><h2 id="记录"><a href="#记录" class="headerlink" title="记录"></a>记录</h2><p>在实践中遇到了很多问题，很多问题当时解决之后不长的一段时间就忘了，想重新去看的时候又不知道在什么地方了，才促使我写点东西来记录一下。当然这篇文章只是一个引子，具体内容还得慢慢写。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;&lt;strong&gt;&lt;center&gt;这篇文章仅做学习过程的记录，不涉及技术问题&lt;/center&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;引言&quot;&gt;&lt;a href=&quot;#引言&quot; class=&quot;headerlink&quot; title=&quot;引言&quot;&gt;&lt;/a&gt;引言&lt;/h2&gt;&lt;p&gt;前段时间确定了
      
    
    </summary>
    
      <category term="Java" scheme="https://hexo.xinsane.com/categories/Java/"/>
    
      <category term="SSM" scheme="https://hexo.xinsane.com/categories/Java/SSM/"/>
    
    
      <category term="Java" scheme="https://hexo.xinsane.com/tags/Java/"/>
    
      <category term="SSM" scheme="https://hexo.xinsane.com/tags/SSM/"/>
    
      <category term="JavaWeb" scheme="https://hexo.xinsane.com/tags/JavaWeb/"/>
    
  </entry>
  
</feed>
