<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>卡卢比-Blog</title>
  
  <subtitle>Reading|Fitness|Coding</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="http://blog.lyq3.com/"/>
  <updated>2019-11-25T03:16:01.323Z</updated>
  <id>http://blog.lyq3.com/</id>
  
  <author>
    <name>卡卢比</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>docker安装redis（哨兵模式）</title>
    <link href="http://blog.lyq3.com/2019/11/22/docker/install-redis-sentinel/"/>
    <id>http://blog.lyq3.com/2019/11/22/docker/install-redis-sentinel/</id>
    <published>2019-11-22T12:43:28.000Z</published>
    <updated>2019-11-25T03:16:01.323Z</updated>
    
    <content type="html"><![CDATA[<h1 id="docker安装redis（哨兵模式）"><a href="#docker安装redis（哨兵模式）" class="headerlink" title="docker安装redis（哨兵模式）"></a>docker安装redis（哨兵模式）</h1><blockquote><p>准备工作，5台本地虚拟机，三台哨兵，2台redis（一主一从），安装好docker环境。（也可以在一台机器上启动5个实例，配置5个不同端口即可）</p></blockquote><h3 id="下载镜像"><a href="#下载镜像" class="headerlink" title="下载镜像"></a>下载镜像</h3><ul><li>搜索redis镜像<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker search redis</span><br></pre></td></tr></table></figure></li></ul><a id="more"></a><p><img src="index_files/81b7daa8-47b9-49c3-af26-e44e6e44530e.png" alt=""></p><ul><li>下载镜像<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker pull docker.io/redis:5.0</span><br></pre></td></tr></table></figure></li></ul><p><img src="index_files/52ce32ff-c79d-4d0a-ae1b-3f9a7c4858cf.png" alt=""></p><blockquote><p>redis 版本可在<a href="https://hub.docker.com/" title="dockerhub" target="_blank" rel="noopener">dockerhub</a>仓库查看</p></blockquote><h3 id="主从配置"><a href="#主从配置" class="headerlink" title="主从配置"></a>主从配置</h3><ul><li>创建数据目录和配置目录<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">mkdir /usr/redis/data</span><br><span class="line">mkdir /usr/redis/config</span><br></pre></td></tr></table></figure></li></ul><blockquote><p>后面将会把容器内的文件挂载出来</p></blockquote><h4 id="主节点配置"><a href="#主节点配置" class="headerlink" title="主节点配置"></a>主节点配置</h4><pre><code>在/usr/redis/config下创建配置文件redis.conf并添加配置参数：</code></pre><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">#是否在后台执行，yes：后台运行；no：不是后台运行</span></span><br><span class="line"><span class="string">daemonize</span> <span class="literal">no</span></span><br><span class="line"><span class="comment">#是否开启保护模式，默认开启。要是配置里没有指定bind和密码。开启该参数后，redis只会本地进行访问，拒绝外部访问。</span></span><br><span class="line"><span class="string">protected-mode</span> <span class="literal">no</span></span><br><span class="line"><span class="comment">#redis监听的端口号。</span></span><br><span class="line"><span class="string">port</span> <span class="number">6379</span></span><br><span class="line"><span class="comment">#指定 redis 只接收来自于该 IP 地址的请求，如果不进行设置，那么将处理所有请求，或者写成0.0.0.0</span></span><br><span class="line"><span class="comment">#bind 127.0.0.1</span></span><br><span class="line"><span class="comment">#指定了记录日志的文件。空字符串的话，日志会打印到标准输出设备。后台运行的redis标准输出是/dev/null。</span></span><br><span class="line"><span class="string">logfile</span> <span class="string">/logs/redis-server.log</span></span><br><span class="line"><span class="comment"># 900秒（15分钟）内至少1个key值改变（则进行数据库保存--持久化）</span></span><br><span class="line"><span class="string">save</span> <span class="number">900</span> <span class="number">1</span></span><br><span class="line"><span class="comment">#当RDB持久化出现错误后，是否依然进行继续进行工作，yes：不能进行工作，no：可以继续进行工作，可以通过info中的rdb_last_bgsave_status了解RDB持久化是否有错误</span></span><br><span class="line"><span class="string">stop-writes-on-bgsave-error</span> <span class="literal">yes</span></span><br><span class="line"><span class="comment">#数据目录，数据库的写入会在这个目录。rdb、aof文件也会写在这个目录</span></span><br><span class="line"><span class="string">dir</span> <span class="string">/data</span></span><br></pre></td></tr></table></figure><h4 id="从节点配置"><a href="#从节点配置" class="headerlink" title="从节点配置"></a>从节点配置</h4><pre><code>除了以上配置项外，从节点还需增加主从相关配置</code></pre><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">#是否在后台执行，yes：后台运行；no：不是后台运行</span></span><br><span class="line"><span class="string">daemonize</span> <span class="literal">no</span></span><br><span class="line"><span class="comment">#是否开启保护模式，默认开启。要是配置里没有指定bind和密码。开启该参数后，redis只会本地进行访问，拒绝外部访问。</span></span><br><span class="line"><span class="string">protected-mode</span> <span class="literal">no</span></span><br><span class="line"><span class="comment">#redis监听的端口号。</span></span><br><span class="line"><span class="string">port</span> <span class="number">6379</span></span><br><span class="line"><span class="comment">#指定 redis 只接收来自于该 IP 地址的请求，如果不进行设置，那么将处理所有请求，或者写成0.0.0.0</span></span><br><span class="line"><span class="comment">#bind 127.0.0.1</span></span><br><span class="line"><span class="comment">#指定了记录日志的文件。空字符串的话，日志会打印到标准输出设备。后台运行的redis标准输出是/dev/null。</span></span><br><span class="line"><span class="string">logfile</span> <span class="string">/logs/redis-server.log</span></span><br><span class="line"><span class="comment"># 900秒（15分钟）内至少1个key值改变（则进行数据库保存--持久化）</span></span><br><span class="line"><span class="string">save</span> <span class="number">900</span> <span class="number">1</span></span><br><span class="line"><span class="comment">#当RDB持久化出现错误后，是否依然进行继续进行工作，yes：不能进行工作，no：可以继续进行工作，可以通过info中的rdb_last_bgsave_status了解RDB持久化是否有错误</span></span><br><span class="line"><span class="string">stop-writes-on-bgsave-error</span> <span class="literal">yes</span></span><br><span class="line"><span class="comment">#数据目录，数据库的写入会在这个目录。rdb、aof文件也会写在这个目录，下面的文件路径将会挂载出去，自定义。</span></span><br><span class="line"><span class="string">dir</span> <span class="string">/data</span></span><br><span class="line"></span><br><span class="line"><span class="comment">############### 主从复制 ###############</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#作为从服务器，默认情况下是只读的（yes），可以修改成NO，用于写（不建议）。</span></span><br><span class="line"><span class="string">slave-read-only</span> <span class="literal">yes</span></span><br><span class="line"><span class="comment"># 设定master的IP和端口号，redis配置文件中的默认端口号是6379</span></span><br><span class="line"><span class="comment"># 低版本的redis这里会是slaveof，意思是一样的，因为slave是比较敏感的词汇，所以在redis后面的版本中不在使用slave的概念，取而代之的是replica</span></span><br><span class="line"><span class="comment"># 将192.168.72.128做为主，其余一台机器做从。ip和端口号按照机器和配置做相应修改。</span></span><br><span class="line"><span class="string">replicaof</span> <span class="number">192.168</span><span class="number">.72</span><span class="number">.128</span> <span class="number">6379</span></span><br></pre></td></tr></table></figure><h3 id="启动容器"><a href="#启动容器" class="headerlink" title="启动容器"></a>启动容器</h3><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></pre></td><td class="code"><pre><span class="line"><span class="string">docker</span> <span class="string">run</span> <span class="bullet">-d</span> <span class="bullet">--name</span> <span class="string">redis</span> <span class="bullet">--restart=always</span> <span class="bullet">-p</span> <span class="number">6379</span><span class="string">:6379</span> <span class="string">\</span></span><br><span class="line"><span class="bullet">-</span><span class="bullet">-privileged=true</span> <span class="string">\</span></span><br><span class="line"><span class="bullet"> -</span><span class="string">v</span> <span class="string">/usr/redis/data/:/data</span> <span class="string">\</span></span><br><span class="line"><span class="bullet"> -</span><span class="string">v</span> <span class="string">/usr/redis/redis.conf:/usr/local/etc/redis/redis.conf</span> <span class="string">\</span></span><br><span class="line"> <span class="number">17</span><span class="string">a9b6c90ffd</span> <span class="string">redis-server</span> <span class="string">/usr/local/etc/redis/redis.conf</span></span><br></pre></td></tr></table></figure><pre><code>参数说明：</code></pre><table><thead><tr><th>参数</th><th>说明</th></tr></thead><tbody><tr><td>-d</td><td>后台运行容器</td></tr><tr><td>–name</td><td>容器名称</td></tr><tr><td>–restart</td><td>开机自动启动容器</td></tr><tr><td>-p</td><td>端口映射</td></tr><tr><td>–privileged=true</td><td>容器内root用户将真正拥有root权限（可不写）</td></tr><tr><td>-v</td><td>文件挂载，格式：外部文件:docker内部文件（与上面配置文件里定义的文件路径保持一致：如dir:/data）</td></tr><tr><td>17a9b6c90ffd</td><td>镜像ID</td></tr><tr><td>redis-server</td><td>redis的启动服务，固定的</td></tr><tr><td>/usr/local/etc/redis/redis.conf</td><td>采用此配置文件启动redis-server服务</td></tr></tbody></table><h4 id="验证主从配置"><a href="#验证主从配置" class="headerlink" title="验证主从配置"></a>验证主从配置</h4><ul><li>进入到容器中<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span> redis是容器名</span><br><span class="line">docker exec -it redis bash</span><br><span class="line"><span class="meta">#</span> 进入redis 客户端,这里没设置密码</span><br><span class="line">redis-cli</span><br><span class="line"><span class="meta">#</span> 查看节点信息</span><br><span class="line">info replication</span><br></pre></td></tr></table></figure></li></ul><p><img src="index_files/87318598-b710-477c-b337-b73351344800.png" alt=""></p><h4 id="遇到的问题"><a href="#遇到的问题" class="headerlink" title="遇到的问题"></a>遇到的问题</h4><ul><li>Can’t open the log file: Permission denied<br><img src="index_files/e7529c0f-7a32-4eb1-9a91-e0ea76ac0f81.png" alt=""><blockquote><p>这是因为没给挂载的文件写权限，给整个redis目录赋权就行了</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">chmod -R 777 /usr/redis</span><br></pre></td></tr></table></figure></blockquote></li></ul><h3 id="添加哨兵集群配置"><a href="#添加哨兵集群配置" class="headerlink" title="添加哨兵集群配置"></a>添加哨兵集群配置</h3><blockquote><p>哨兵本质上也是redis实例，只是配置不同而已，起到监控redis主从的作用，当主节点挂掉后将从节点提升为主节点，当主节点重新恢复后自动将其作为从节点加入到集群中。起到保证集群高可用的作用</p></blockquote><h4 id="添加配置文件"><a href="#添加配置文件" class="headerlink" title="添加配置文件"></a>添加配置文件</h4><blockquote><p>参考官方配置文件：<a href="http://download.redis.io/redis-stable/sentinel.conf" target="_blank" rel="noopener">http://download.redis.io/redis-stable/sentinel.conf</a></p></blockquote><ul><li>修改配置</li></ul><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#是否在后台执行，yes：后台运行；no：不是后台运行</span></span><br><span class="line"><span class="string">daemonize</span> <span class="literal">no</span></span><br><span class="line"><span class="comment">#是否开启保护模式，默认开启。要是配置里没有指定bind和密码。开启该参数后，redis只会本地进行访问，拒绝外部访问。</span></span><br><span class="line"><span class="string">protected-mode</span> <span class="literal">no</span></span><br><span class="line"><span class="comment">#redis监听的端口号。</span></span><br><span class="line"><span class="string">port</span> <span class="number">6379</span></span><br><span class="line"><span class="comment">#指定 redis 只接收来自于该 IP 地址的请求，如果不进行设置，那么将处理所有请求</span></span><br><span class="line"><span class="comment">#bind 127.0.0.1</span></span><br><span class="line"><span class="comment">#指定了记录日志的文件。空字符串的话，日志会打印到标准输出设备。后台运行的redis标准输出是/dev/null。</span></span><br><span class="line"><span class="string">logfile</span> <span class="string">/logs/redis-server.log</span></span><br><span class="line"><span class="comment"># 900秒（15分钟）内至少1个key值改变（则进行数据库保存--持久化）</span></span><br><span class="line"><span class="string">save</span> <span class="number">900</span> <span class="number">1</span></span><br><span class="line"><span class="comment">#当RDB持久化出现错误后，是否依然进行继续进行工作，yes：不能进行工作，no：可以继续进行工作，可以通过info中的rdb_last_bgsave_status了解RDB持久化是否有错误</span></span><br><span class="line"><span class="string">stop-writes-on-bgsave-error</span> <span class="literal">yes</span></span><br><span class="line"><span class="comment">#数据目录，数据库的写入会在这个目录。rdb、aof文件也会写在这个目录</span></span><br><span class="line"><span class="string">dir</span> <span class="string">/data</span></span><br><span class="line"><span class="comment">#设置AOF模式开启（持久化）</span></span><br><span class="line"><span class="string">appendonly</span> <span class="literal">yes</span></span><br><span class="line"></span><br><span class="line"><span class="comment">##############哨兵#################</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 最后一个2表示，两台机器判定主被动下线后，就进行failover(故障转移)，quorum是一个数字，指明当有多少个sentinel认为一个master失效时，master才算真正失效</span></span><br><span class="line"><span class="string">sentinel</span> <span class="string">monitor</span> <span class="string">mymaster</span> <span class="number">192.168</span><span class="number">.72</span><span class="number">.128</span> <span class="number">6379</span> <span class="number">2</span></span><br><span class="line"><span class="comment"># 同时监控其他两台哨兵机器</span></span><br><span class="line"><span class="comment">#sentinel auth-pass &lt;master-name&gt; &lt;password&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#这个配置项指定了需要多少失效时间，一个master才会被这个sentinel主观地认为是不可用的。 单位是毫秒，默认为30秒</span></span><br><span class="line"><span class="string">sentinel</span> <span class="string">down-after-milliseconds</span> <span class="string">mymaster</span> <span class="number">10000</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#failover-timeout 可以用在以下这些方面：     </span></span><br><span class="line"><span class="comment">#1. 同一个sentinel对同一个master两次failover之间的间隔时间。   </span></span><br><span class="line"><span class="comment">#2. 当一个slave从一个错误的master那里同步数据开始计算时间。直到slave被纠正为向正确的master那里同步数据时。    </span></span><br><span class="line"><span class="comment">#3.当想要取消一个正在进行的failover所需要的时间。    </span></span><br><span class="line"><span class="comment">#4.当进行failover时，配置所有slaves指向新的master所需的最大时间。不过，即使过了这个超时，slaves依然会被正确配置为指向master，但是就不按parallel-syncs所配置的规则来了。</span></span><br><span class="line"><span class="string">sentinel</span> <span class="string">failover-timeout</span> <span class="string">mymaster</span> <span class="number">180000</span></span><br></pre></td></tr></table></figure><ul><li>启动哨兵<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span> 和启动主从类似，只需要把redis-server改为redis-sentinel即可</span><br><span class="line"> docker run -d --name redis --restart=always -p 6379:6379 \</span><br><span class="line"> --privileged=true  -v /usr/redis/data:/data  \</span><br><span class="line"> -v /usr/redis/config/redis.conf:/usr/local/etc/redis/redis.conf  \</span><br><span class="line"> -v /usr/redis/logs/redis-server.log:/logs/redis-server.log  \</span><br><span class="line"> 17a9b6c90ffd redis-sentinel /usr/local/etc/redis/redis.conf</span><br></pre></td></tr></table></figure></li></ul><h4 id="查看哨兵是否启动成功"><a href="#查看哨兵是否启动成功" class="headerlink" title="查看哨兵是否启动成功"></a>查看哨兵是否启动成功</h4><blockquote><p>再次打开哨兵的配置文件，将会看到文件被改写，增加了3台哨兵之间的互相监控配置</p></blockquote><p><img src="index_files/0076475e-cba7-48be-8b5f-1b5d03f71abb.png" alt=""></p><h4 id="验证哨兵的自动故障转移"><a href="#验证哨兵的自动故障转移" class="headerlink" title="验证哨兵的自动故障转移"></a>验证哨兵的自动故障转移</h4><ul><li>先查看从节点信息<br><img src="index_files/07166fb9-83ca-4fd9-a02e-40bd6887f9ba.png" alt=""></li><li><p>关闭主节点</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker stop redis</span><br></pre></td></tr></table></figure></li><li><p>等一会再次查看从节点信息<br><img src="index_files/5ed28a2e-d3a5-44a8-be9f-6cc0db29812f.png" alt=""></p></li></ul><blockquote><p>从节点自动变成了master节点。实现了自动转移</p></blockquote><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><blockquote><p>填写配置文件一定要仔细，挂载文件也要仔细，即使挂载的文件不正确redis也能正常启动并采用默认的配置，导致主从配置不生效</p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;docker安装redis（哨兵模式）&quot;&gt;&lt;a href=&quot;#docker安装redis（哨兵模式）&quot; class=&quot;headerlink&quot; title=&quot;docker安装redis（哨兵模式）&quot;&gt;&lt;/a&gt;docker安装redis（哨兵模式）&lt;/h1&gt;&lt;blockquote&gt;
&lt;p&gt;准备工作，5台本地虚拟机，三台哨兵，2台redis（一主一从），安装好docker环境。（也可以在一台机器上启动5个实例，配置5个不同端口即可）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;下载镜像&quot;&gt;&lt;a href=&quot;#下载镜像&quot; class=&quot;headerlink&quot; title=&quot;下载镜像&quot;&gt;&lt;/a&gt;下载镜像&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;搜索redis镜像&lt;figure class=&quot;highlight shell&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;docker search redis&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;/ul&gt;
    
    </summary>
    
      <category term="docker" scheme="http://blog.lyq3.com/categories/docker/"/>
    
    
      <category term="docker" scheme="http://blog.lyq3.com/tags/docker/"/>
    
  </entry>
  
  <entry>
    <title>ES查询DSL之全文查询</title>
    <link href="http://blog.lyq3.com/2019/11/11/elasticsearch/DSL-FullTextQueries/"/>
    <id>http://blog.lyq3.com/2019/11/11/elasticsearch/DSL-FullTextQueries/</id>
    <published>2019-11-11T12:43:28.000Z</published>
    <updated>2019-11-18T14:06:50.425Z</updated>
    
    <content type="html"><![CDATA[<h3 id="全文搜索"><a href="#全文搜索" class="headerlink" title="全文搜索"></a>全文搜索</h3><h4 id="间隔查询（intervals）"><a href="#间隔查询（intervals）" class="headerlink" title="间隔查询（intervals）"></a>间隔查询（intervals）</h4><blockquote><p>7.X的新特性，查询给定词组匹配指定间隔内的文档，例如：太极藿香，将分成 “太极” 和 “藿香”。可以设置两个词之间最多可有多大间隔才能匹配。若间隔设置为2，则“太极藿香正气液”间隔为0，可以匹配。<br><a id="more"></a><br>例：<br><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">&#123;</span><br><span class="line">  <span class="attr">"query"</span>: &#123;</span><br><span class="line">    <span class="attr">"intervals"</span> : &#123;</span><br><span class="line">      <span class="attr">"name"</span> : &#123;</span><br><span class="line">        <span class="attr">"all_of"</span> : &#123;</span><br><span class="line">          <span class="attr">"ordered"</span> : <span class="literal">true</span>,</span><br><span class="line">          <span class="attr">"intervals"</span> : [</span><br><span class="line">            &#123;</span><br><span class="line">              <span class="attr">"match"</span> : &#123;</span><br><span class="line">                <span class="attr">"query"</span> : <span class="string">"太极藿香"</span>,</span><br><span class="line">                <span class="attr">"max_gaps"</span> : <span class="number">1</span>,</span><br><span class="line">                <span class="attr">"ordered"</span> : <span class="literal">true</span></span><br><span class="line">              &#125;</span><br><span class="line">            &#125;,</span><br><span class="line">            &#123;</span><br><span class="line">              <span class="attr">"any_of"</span>: &#123;</span><br><span class="line">                  <span class="attr">"intervals"</span>: [</span><br><span class="line">                      &#123;</span><br><span class="line">                          <span class="attr">"match"</span>: &#123;</span><br><span class="line">                              <span class="attr">"query"</span>: <span class="string">"口服液"</span>,</span><br><span class="line">                              <span class="attr">"max_gaps"</span>:<span class="number">-1</span></span><br><span class="line">                          &#125;</span><br><span class="line">                      &#125;,</span><br><span class="line">                      &#123;</span><br><span class="line">                          <span class="attr">"match"</span>: &#123;</span><br><span class="line">                              <span class="attr">"query"</span>: <span class="string">"匹配任一个"</span>,</span><br><span class="line">                              <span class="attr">"max_gaps"</span>:<span class="number">-1</span></span><br><span class="line">                          &#125;</span><br><span class="line">                      &#125;</span><br><span class="line">                  ]</span><br><span class="line">              &#125;</span><br><span class="line">          &#125;</span><br><span class="line">          ]</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p></blockquote><ul><li><p>顶级参数 intervals </p><figure class="highlight"><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">"intervals":&#123;</span><br><span class="line">    "field":&#123;规则&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  field: 必填，要搜索的字段名<br>  值是一个规则字段，字段可选规则如下：</p></li></ul><table><thead><tr><th>规则</th></tr></thead><tbody><tr><td>match</td></tr><tr><td>prefix</td></tr><tr><td>wildcard</td></tr><tr><td>all_of</td></tr><tr><td>any_of</td></tr><tr><td>filter</td></tr></tbody></table><ul><li>match规则参数<blockquote><p>按match规则匹配分析文档</p></blockquote></li></ul><table><thead><tr><th>参数</th><th>说明</th><th>是否必须</th></tr></thead><tbody><tr><td>query</td><td>查询的关键字</td><td>是</td></tr><tr><td>max_gaps</td><td>匹配项之间的最大位置数，默认-1（不限），若为0则两个词必须相邻</td><td>否</td></tr><tr><td>ordered</td><td>如果为true，则匹配词必须以指定顺序出现，默认为false</td><td>否</td></tr><tr><td>analyzer</td><td>分词器,不指定默认为field的分词器</td><td>否</td></tr><tr><td>filter</td><td>间隔过滤器（<a href="https://www.elastic.co/guide/en/elasticsearch/reference/7.3/query-dsl-intervals-query.html#interval_filter" target="_blank" rel="noopener">https://www.elastic.co/guide/en/elasticsearch/reference/7.3/query-dsl-intervals-query.html#interval_filter</a>）</td><td>否</td></tr><tr><td>use_field</td><td>使用该字段中的搜索分析器分析术语。而不是使用顶级参数中指定的字段（field参数）这使您可以跨多个字段搜索</td><td>否</td></tr></tbody></table><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">&#123;</span><br><span class="line">  <span class="attr">"query"</span>: &#123;</span><br><span class="line">    <span class="attr">"intervals"</span> : &#123;</span><br><span class="line">      <span class="attr">"name"</span> : &#123;</span><br><span class="line">        <span class="attr">"match"</span> : &#123;</span><br><span class="line">              <span class="attr">"query"</span> : <span class="string">"太极藿香"</span>,</span><br><span class="line">              <span class="attr">"ordered"</span>: <span class="literal">true</span>,</span><br><span class="line">              <span class="attr">"max_gaps"</span>:<span class="number">1</span>,</span><br><span class="line">              <span class="attr">"use_field"</span>:<span class="string">"productGeneralName"</span></span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>prefix 规则参数<blockquote><p>该prefix规则匹配以指定字符集开头的文档，该前缀可以扩展以匹配最多128个词(terms),超过128个返回错误也可以使用<a href="https://www.elastic.co/guide/en/elasticsearch/reference/7.3/index-prefixes.html" title="index_prefixes" target="_blank" rel="noopener"><code>index-prefixes</code></a>限制、</p></blockquote></li></ul><table><thead><tr><th>参数</th><th>类型</th><th>说明</th><th>是否必须</th></tr></thead><tbody><tr><td>prefix</td><td>string</td><td>需要查询的前缀</td><td>是</td></tr><tr><td>analyzer</td><td>string</td><td>分词器，默认field的分词器</td><td>否</td></tr><tr><td>use_field</td><td>string</td><td>搜索该字段，而不是顶级参数中指定的field</td><td>否</td></tr></tbody></table><ul><li>wildcard 规则参数<blockquote><p>使用通配符模式匹配文档，最多可匹配128个词（terms）,如果超出则返回错误</p></blockquote></li></ul><table><thead><tr><th>参数</th><th>类型</th><th>说明</th><th>是否必须</th></tr></thead><tbody><tr><td>pattern</td><td>string</td><td>通配符文本（正则）</td><td>是</td></tr><tr><td>analyzer</td><td>string</td><td>分词器，默认顶级参数field的分词器</td><td>否</td></tr><tr><td>use_field</td><td>string</td><td>匹配该字段，而不是顶级参数中指定的field</td><td>否</td></tr></tbody></table><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">&#123;</span><br><span class="line">  <span class="attr">"query"</span>: &#123;</span><br><span class="line">    <span class="attr">"intervals"</span> : &#123;</span><br><span class="line">      <span class="attr">"name"</span> : &#123;</span><br><span class="line">        <span class="attr">"wildcard"</span> : &#123;</span><br><span class="line">              <span class="attr">"pattern"</span> : <span class="string">"龙胆?*"</span>,</span><br><span class="line">              <span class="attr">"use_field"</span>:<span class="string">"productGeneralName"</span></span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>all_of规则参数<blockquote><p>多用于嵌套查询，返回同时匹配多个组合规则的文档</p></blockquote></li></ul><table><thead><tr><th>参数</th><th>类型</th><th>说明</th><th>是否必须</th></tr></thead><tbody><tr><td>intervals</td><td>对象数组</td><td>要组合的规则数组，所有规则都必须匹配才会返回</td><td>是</td></tr><tr><td>max_gaps</td><td>整数</td><td>匹配项之间的最大位置数，</td><td>否，默认-1</td></tr><tr><td>ordered</td><td>布尔值</td><td>是否按规则指定顺序出现</td><td>否，默认false</td></tr><tr><td>filter</td><td><a href="https://www.elastic.co/guide/en/elasticsearch/reference/7.3/query-dsl-intervals-query.html#interval_filter" title="过滤规则参数" target="_blank" rel="noopener">间隔过滤器对象</a></td><td>间隔过滤器</td><td>否</td></tr></tbody></table><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">"query"</span>: &#123;</span><br><span class="line">    <span class="attr">"intervals"</span> : &#123;</span><br><span class="line">      <span class="attr">"name"</span> : &#123;</span><br><span class="line">        <span class="attr">"all_of"</span>:</span><br><span class="line">        &#123;</span><br><span class="line">          <span class="attr">"max_gaps"</span>:<span class="number">2</span>,</span><br><span class="line">          <span class="attr">"ordered"</span>:<span class="literal">true</span>,</span><br><span class="line">          <span class="attr">"intervals"</span>:[</span><br><span class="line">          &#123;</span><br><span class="line">            <span class="attr">"match"</span> : &#123;</span><br><span class="line">                <span class="attr">"query"</span> : <span class="string">"复方龙胆"</span>,</span><br><span class="line">                <span class="attr">"use_field"</span>:<span class="string">"productGeneralName"</span></span><br><span class="line">          &#125;</span><br><span class="line">          &#125;,</span><br><span class="line">           &#123;</span><br><span class="line">            <span class="attr">"match"</span> : &#123;</span><br><span class="line">                <span class="attr">"query"</span> : <span class="string">"氢钠碳酸"</span>,</span><br><span class="line">                <span class="attr">"max_gaps"</span>:<span class="number">1</span>,</span><br><span class="line">                <span class="attr">"ordered"</span>:<span class="literal">false</span>,</span><br><span class="line">                <span class="attr">"use_field"</span>:<span class="string">"productGeneralName"</span></span><br><span class="line">          &#125;</span><br><span class="line">          &#125;</span><br><span class="line">          ]</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>any_of规则参数<blockquote><p>返回匹配规则组中任何一组规则的文档</p></blockquote></li></ul><table><thead><tr><th>参数</th><th>类型</th><th>说明</th><th>是否必须</th></tr></thead><tbody><tr><td>intervals</td><td>规则对象数组</td><td>要组合的规则数组，匹配任一项即可</td><td>是</td></tr><tr><td>filter</td><td><a href="https://www.elastic.co/guide/en/elasticsearch/reference/7.3/query-dsl-intervals-query.html#interval_filter" title="过滤规则参数" target="_blank" rel="noopener">间隔过滤器对象</a></td><td>间隔过滤器</td><td>否</td></tr></tbody></table><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">"query"</span>: &#123;</span><br><span class="line">    <span class="attr">"intervals"</span> : &#123;</span><br><span class="line">      <span class="attr">"name"</span> : &#123;</span><br><span class="line">        <span class="attr">"any_of"</span>:</span><br><span class="line">        &#123;</span><br><span class="line">          <span class="attr">"intervals"</span>:[</span><br><span class="line">          &#123;</span><br><span class="line">            <span class="attr">"match"</span> : &#123;</span><br><span class="line">                <span class="attr">"query"</span> : <span class="string">"复方龙胆片"</span>,</span><br><span class="line">                <span class="attr">"ordered"</span>:<span class="literal">true</span>,</span><br><span class="line">                <span class="attr">"max_gaps"</span>:<span class="number">0</span></span><br><span class="line">          &#125;</span><br><span class="line">          &#125;,</span><br><span class="line">           &#123;</span><br><span class="line">            <span class="attr">"match"</span> : &#123;</span><br><span class="line">                <span class="attr">"query"</span> : <span class="string">"太极藿香正气"</span>,</span><br><span class="line">                <span class="attr">"max_gaps"</span>:<span class="number">1</span>,</span><br><span class="line">                <span class="attr">"ordered"</span>:<span class="literal">false</span>,</span><br><span class="line">                <span class="attr">"use_field"</span>:<span class="string">"productGeneralName"</span></span><br><span class="line">          &#125;</span><br><span class="line">          &#125;</span><br><span class="line">          ]</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>filter规则参数<blockquote><p>返回基于查询的时间间隔，详情参考：<br>规则文档：<a href="https://www.elastic.co/guide/en/elasticsearch/reference/7.3/query-dsl-intervals-query.html#intervals-top-level-params" target="_blank" rel="noopener">https://www.elastic.co/guide/en/elasticsearch/reference/7.3/query-dsl-intervals-query.html#intervals-top-level-params</a><br>间隔过滤器文档：<a href="https://www.elastic.co/guide/en/elasticsearch/reference/7.3/query-dsl-intervals-query.html#interval-filter-rule-ex" target="_blank" rel="noopener">https://www.elastic.co/guide/en/elasticsearch/reference/7.3/query-dsl-intervals-query.html#interval-filter-rule-ex</a></p></blockquote></li></ul><h4 id="匹配查询（match）"><a href="#匹配查询（match）" class="headerlink" title="匹配查询（match）"></a>匹配查询（match）</h4><blockquote><p>ES的标准查询，用于匹配搜索的文字，模糊查询</p></blockquote><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">GET /_search</span><br><span class="line">&#123;</span><br><span class="line">    <span class="attr">"query"</span>: &#123;</span><br><span class="line">        <span class="attr">"match"</span> : &#123;</span><br><span class="line">            <span class="attr">"productName"</span> : &#123;</span><br><span class="line">                <span class="attr">"query"</span> : <span class="string">"阿莫西林"</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>首先以query开头，查询方式为match，字段为productName<br>然后设置字段查询相关属性：</p><ul><li>query（必填）<blockquote><p>查询的内容。</p></blockquote></li><li>analyzer（可选，字符串）<blockquote><p>分词器，输入的keywords分词，若不设置则使用索引的分词器</p></blockquote></li><li>auto_generate_synonyms_phrase_query（可选，布尔值，默认为true）<blockquote><p>是否开启近义词组合查询</p></blockquote></li><li>fuzziness（可选，字符串）<blockquote><p>模糊性，解决一些拼写错误的场景，word 被写成world,那么要把world转换成word只需要将l删除，那么就是一次编辑，距离就是1，经过几次编辑距离就是几。该参数用于设置匹配允许的最大编辑距离，参考：<a href="https://www.elastic.co/guide/en/elasticsearch/reference/7.3/common-options.html#fuzziness" target="_blank" rel="noopener">https://www.elastic.co/guide/en/elasticsearch/reference/7.3/common-options.html#fuzziness</a></p></blockquote></li><li>max_expansions（可选，整数，默认50）<blockquote><p>terms查询能扩展的最大数</p></blockquote></li><li>prefix_length（可选，整数，默认0）<blockquote><p>为模糊匹配保留的起始字符数，配合fuzziness使用的，保留前几个字符精确匹配，后面的模糊</p></blockquote></li><li>transpositions（可选，布尔值，默认true）<blockquote><p>模糊匹配的编辑内容包括两个相邻字符的转置（ab→ba），如wodr转为word只需要转换一次，即为一次编辑，配合fuzziness使用</p></blockquote></li><li>fuzzy_rewrite（可选，字符串）<blockquote><p>用于重写查询的方法，配合fuzziness使用</p></blockquote></li><li>lenient（可选，布尔值，默认false）<blockquote><p>是否忽略格式错误，例如：数字字段输入字符串</p></blockquote></li><li>operator（可选，字符串，默认OR）<blockquote><p>值为OR或AND，比如搜索：i like you 会解析成：i OR like OR you，就是搜索词组是否全部要包含，and将包含所有词，OR只需要包含任一词就行</p></blockquote></li><li>minimum_should_match（可选，字符串）<blockquote><p>要返回的文档必须匹配的最小子句数：可填数字，百分比：参考：<a href="https://www.elastic.co/guide/en/elasticsearch/reference/7.3/query-dsl-minimum-should-match.html" target="_blank" rel="noopener">https://www.elastic.co/guide/en/elasticsearch/reference/7.3/query-dsl-minimum-should-match.html</a></p></blockquote></li><li>zero_terms_query（可选，字符串,默认none）<blockquote><p>指示如果analyzer 删除所有标记（例如使用stop过滤器时），是否不返回任何文档,可选值：none、all，参考：<a href="https://www.elastic.co/guide/en/elasticsearch/reference/7.3/query-dsl-match-query.html#query-dsl-match-query-zero" target="_blank" rel="noopener">零条件查询</a></p></blockquote></li></ul><h4 id="match-bool-prefix-查询"><a href="#match-bool-prefix-查询" class="headerlink" title="match_bool_prefix 查询"></a>match_bool_prefix 查询</h4><blockquote><p>该查询会构造成一组bool组合查询，将最后一个词作为前缀查询.</p></blockquote><pre><code>例如：</code></pre><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">&#123;</span><br><span class="line">    <span class="attr">"query"</span>: &#123;</span><br><span class="line">        <span class="attr">"match_bool_prefix"</span> : &#123;</span><br><span class="line">            <span class="attr">"message"</span> : <span class="string">"quick brown f"</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><pre><code>以上查询相当于下面的查询：</code></pre><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="attr">"query"</span>: &#123;</span><br><span class="line">        <span class="attr">"bool"</span> : &#123;</span><br><span class="line">            <span class="attr">"should"</span>: [</span><br><span class="line">                &#123; <span class="attr">"term"</span>: &#123; <span class="attr">"message"</span>: <span class="string">"quick"</span> &#125;&#125;,</span><br><span class="line">                &#123; <span class="attr">"term"</span>: &#123; <span class="attr">"message"</span>: <span class="string">"brown"</span> &#125;&#125;,</span><br><span class="line">                &#123; <span class="attr">"prefix"</span>: &#123; <span class="attr">"message"</span>: <span class="string">"f"</span>&#125;&#125;</span><br><span class="line">            ]</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>前面的分词会被作为term查询，相当于普通分词查询，后面的作为前缀查询（prefix），使用should,满足任何一个条件就会命中</p></blockquote><h4 id="匹配词组查询（Match-phrase-query）"><a href="#匹配词组查询（Match-phrase-query）" class="headerlink" title="匹配词组查询（Match phrase query）"></a>匹配词组查询（Match phrase query）</h4><blockquote><p>短语匹配，会将输入的搜索词分词后一 一匹配，当所有词语都匹配上时才返回，并且匹配的词语是挨着的(可通过slop调节)。相当于整句匹配</p></blockquote><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">&#123;</span><br><span class="line">  <span class="attr">"query"</span>: &#123;</span><br><span class="line">    <span class="attr">"match_phrase"</span>: &#123;</span><br><span class="line">      <span class="attr">"name"</span>: &#123;</span><br><span class="line">        <span class="attr">"query"</span>: <span class="string">"藿香正气液"</span>,</span><br><span class="line">        <span class="attr">"slop"</span>: <span class="number">0</span></span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>上面只会返回包含“藿香正气液”整句的文档，可通过slop设置两个词之间的间隔位置（默认0，就是紧挨着），也可analyzer指定分词器。</p></blockquote><pre><code>不调整参数使用默认的可以简写：</code></pre><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">&#123;</span><br><span class="line">  <span class="attr">"query"</span>: &#123;</span><br><span class="line">    <span class="attr">"match_phrase"</span>: &#123;</span><br><span class="line">      <span class="attr">"name"</span>: <span class="string">"藿香正气液"</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="匹配词组前缀查询"><a href="#匹配词组前缀查询" class="headerlink" title="匹配词组前缀查询"></a>匹配词组前缀查询</h4><blockquote><p>与短语匹配查询（Match phrase query）类似，只不过会把最后一个词作为前缀，类似于补全句子功能，比如：搜索quick brown f 就会匹配到quick brown fox结果，f就是fox的前缀，一般用于即时搜索，用于实时的查询结果。</p></blockquote><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">&#123;</span><br><span class="line">    <span class="attr">"query"</span>: &#123;</span><br><span class="line">        <span class="attr">"match_phrase_prefix"</span> : &#123;</span><br><span class="line">            <span class="attr">"message"</span> : &#123;</span><br><span class="line">                <span class="attr">"query"</span> : <span class="string">"quick brown f"</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><table><thead><tr><th>参数</th><th>类型</th><th>说明</th><th>是否必须</th></tr></thead><tbody><tr><td>query</td><td>字符串</td><td>输入的文本</td><td>是</td></tr><tr><td>analyzer</td><td>字符串</td><td>分词器，未设置则使用默认的分词器</td><td>否</td></tr><tr><td>max_expansions</td><td>整数</td><td></td><td>否</td></tr><tr><td>slop</td><td>整数</td><td>匹配令牌之间允许的最大位置数。默认为0。转置字词的斜率为2</td><td>否</td></tr><tr><td>zero_terms_query</td><td>字符串</td><td>指示如果analyzer 删除所有标记（例如使用stop过滤器时），是否不返回任何文档。有效值为： none （默认） 如果analyzer删除所有标记，则不会返回任何文档。 all 返回所有文档，类似于match_all 查询</td><td>否</td></tr></tbody></table><h4 id="多匹配查询"><a href="#多匹配查询" class="headerlink" title="多匹配查询"></a>多匹配查询</h4><blockquote><p>该multi_match查询基于该match查询， 以允许多字段查询</p></blockquote><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">"query"</span>: &#123;</span><br><span class="line">    <span class="attr">"multi_match"</span> : &#123;</span><br><span class="line">      <span class="attr">"query"</span>:    <span class="string">"阿莫西林"</span>, </span><br><span class="line">      <span class="attr">"fields"</span>: [ <span class="string">"name"</span>, <span class="string">"productGeneralName"</span> ] </span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>字段可以使用通配符</p></blockquote><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">"query"</span>: &#123;</span><br><span class="line">    <span class="attr">"multi_match"</span> : &#123;</span><br><span class="line">      <span class="attr">"query"</span>:    <span class="string">"阿莫西林"</span>, </span><br><span class="line">      <span class="attr">"fields"</span>: [ <span class="string">"name"</span>, <span class="string">"*Name"</span> ] </span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>可使用“ ^n ”来增强各个字段提升得分，如下面将name得分提高三倍</p></blockquote><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">"query"</span>: &#123;</span><br><span class="line">    <span class="attr">"multi_match"</span> : &#123;</span><br><span class="line">      <span class="attr">"query"</span>:    <span class="string">"阿莫西林"</span>, </span><br><span class="line">      <span class="attr">"fields"</span>: [ <span class="string">"name^3"</span>, <span class="string">"*Name"</span> ] </span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>multi_match 查询类型</li></ul><table><thead><tr><th>类型</th><th>说明</th></tr></thead><tbody><tr><td>best_fields(默认)</td><td>使用字段匹配度最高的记录，比如上面name字段匹配度更高，则使用name字段所在文档</td></tr><tr><td>most_fields</td><td>合并多个匹配字段的得分。得分高的排前面</td></tr><tr><td>cross_fields</td><td></td></tr><tr><td>phrase</td><td>match_phrase（短语匹配）每个字段，使用分数最高的文档</td></tr><tr><td>phrase_prefix</td><td>match_phrase_prefix（短语前缀匹配）每个字段，使用得分最高的文档</td></tr><tr><td>bool_prefix</td><td>match_bool_prefix 每个字段，并合并每个字段得分</td></tr></tbody></table><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">&#123;</span><br><span class="line">  <span class="attr">"query"</span>: &#123;</span><br><span class="line">    <span class="attr">"multi_match"</span> : &#123;</span><br><span class="line">      <span class="attr">"query"</span>:      <span class="string">"Will Smith"</span>,</span><br><span class="line">      <span class="attr">"type"</span>:       <span class="string">"cross_fields"</span>,</span><br><span class="line">      <span class="attr">"fields"</span>:     [ <span class="string">"first_name"</span>, <span class="string">"last_name"</span> ],</span><br><span class="line">      <span class="attr">"operator"</span>:   <span class="string">"and"</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">"query"</span>: &#123;</span><br><span class="line">    <span class="attr">"bool"</span>: &#123;</span><br><span class="line">      <span class="attr">"should"</span>: [</span><br><span class="line">        &#123;</span><br><span class="line">          <span class="attr">"multi_match"</span> : &#123;</span><br><span class="line">            <span class="attr">"query"</span>:      <span class="string">"Will Smith"</span>,</span><br><span class="line">            <span class="attr">"type"</span>:       <span class="string">"cross_fields"</span>,</span><br><span class="line">            <span class="attr">"fields"</span>:     [ <span class="string">"first"</span>, <span class="string">"last"</span> ],</span><br><span class="line">            <span class="attr">"minimum_should_match"</span>: <span class="string">"50%"</span> </span><br><span class="line">          &#125;</span><br><span class="line">        &#125;,</span><br><span class="line">        &#123;</span><br><span class="line">          <span class="attr">"multi_match"</span> : &#123;</span><br><span class="line">            <span class="attr">"query"</span>:      <span class="string">"Will Smith"</span>,</span><br><span class="line">            <span class="attr">"type"</span>:       <span class="string">"cross_fields"</span>,</span><br><span class="line">            <span class="attr">"fields"</span>:     [ <span class="string">"*.edge"</span> ]</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;</span><br><span class="line">      ]</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      &lt;h3 id=&quot;全文搜索&quot;&gt;&lt;a href=&quot;#全文搜索&quot; class=&quot;headerlink&quot; title=&quot;全文搜索&quot;&gt;&lt;/a&gt;全文搜索&lt;/h3&gt;&lt;h4 id=&quot;间隔查询（intervals）&quot;&gt;&lt;a href=&quot;#间隔查询（intervals）&quot; class=&quot;headerlink&quot; title=&quot;间隔查询（intervals）&quot;&gt;&lt;/a&gt;间隔查询（intervals）&lt;/h4&gt;&lt;blockquote&gt;
&lt;p&gt;7.X的新特性，查询给定词组匹配指定间隔内的文档，例如：太极藿香，将分成 “太极” 和 “藿香”。可以设置两个词之间最多可有多大间隔才能匹配。若间隔设置为2，则“太极藿香正气液”间隔为0，可以匹配。&lt;br&gt;
    
    </summary>
    
      <category term="ElasticSearch" scheme="http://blog.lyq3.com/categories/ElasticSearch/"/>
    
      <category term="DSL" scheme="http://blog.lyq3.com/categories/ElasticSearch/DSL/"/>
    
    
      <category term="ElasticSearch" scheme="http://blog.lyq3.com/tags/ElasticSearch/"/>
    
  </entry>
  
  <entry>
    <title>ES查询DSL之复合查询</title>
    <link href="http://blog.lyq3.com/2019/11/01/elasticsearch/DSL-CompoundQueries/"/>
    <id>http://blog.lyq3.com/2019/11/01/elasticsearch/DSL-CompoundQueries/</id>
    <published>2019-11-01T12:43:28.000Z</published>
    <updated>2019-11-18T14:14:40.349Z</updated>
    
    <content type="html"><![CDATA[<h3 id="复合查询"><a href="#复合查询" class="headerlink" title="复合查询"></a>复合查询</h3><h4 id="bool-查询"><a href="#bool-查询" class="headerlink" title="bool 查询"></a>bool 查询</h4><blockquote><p>组合查询，组合多个查询类型</p></blockquote><table><thead><tr><th>类型</th><th>说明</th><th>计分</th></tr></thead><tbody><tr><td>must</td><td>字句必须出现在文档中</td><td>是</td></tr><tr><td>filter</td><td>字句必须匹配，如：status=1,在filter上下文中执行，不参与计分</td><td>否</td></tr><tr><td>should</td><td>字句应该出现在文档中</td><td>是</td></tr><tr><td>must_not</td><td>字句不得出现在文档中，在filter上下文中执行</td><td>否</td></tr></tbody></table><a id="more"></a><p>例子：<br><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">&#123;</span><br><span class="line">  <span class="attr">"query"</span>: &#123; </span><br><span class="line">    <span class="attr">"bool"</span>: &#123; </span><br><span class="line">      <span class="attr">"must"</span>: [</span><br><span class="line">        &#123; <span class="attr">"match"</span>: &#123; <span class="attr">"name"</span>:   <span class="string">"液"</span>        &#125;&#125;,</span><br><span class="line">        &#123; <span class="attr">"match"</span>: &#123; <span class="attr">"productGeneralName"</span>: <span class="string">"液"</span> &#125;&#125;</span><br><span class="line">      ],</span><br><span class="line">      <span class="attr">"filter"</span>: [ </span><br><span class="line">        &#123; <span class="attr">"term"</span>:  &#123; <span class="attr">"status"</span>: <span class="string">"1"</span> &#125;&#125;</span><br><span class="line">      ],</span><br><span class="line">      <span class="attr">"should"</span>:[&#123;</span><br><span class="line">        <span class="attr">"term"</span>: &#123;<span class="attr">"name"</span>: <span class="string">"太极"</span>&#125;</span><br><span class="line">      &#125;,&#123;</span><br><span class="line">        <span class="attr">"term"</span>:&#123;<span class="attr">"name"</span>:<span class="string">"哟西"</span>&#125;</span><br><span class="line">      &#125;</span><br><span class="line">      ],</span><br><span class="line">      <span class="attr">"must_not"</span>: [</span><br><span class="line">        &#123;<span class="attr">"match"</span>: &#123;</span><br><span class="line">          <span class="attr">"name"</span>: <span class="string">"筋骨"</span></span><br><span class="line">        &#125;&#125;</span><br><span class="line">      ]</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>结果：<br><img src="index_files/9fa9b106-4e3c-4894-b3c0-95e5a32a1067.png" alt=""></p><blockquote><p>bool采用“更好匹配”的原则，匹配条件越多的分数越高越靠前，must或should子句的得分将加在一起，通过_score返回最终得分，filter上下文中执行的条件（filter和must_not）得分为0</p></blockquote><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">"query"</span>: &#123; </span><br><span class="line">    <span class="attr">"bool"</span>: &#123; </span><br><span class="line">     </span><br><span class="line">      <span class="attr">"filter"</span>: [ </span><br><span class="line">        &#123; <span class="attr">"term"</span>:  &#123; <span class="attr">"status"</span>: <span class="string">"1"</span> &#125;&#125;</span><br><span class="line">      ],</span><br><span class="line">      <span class="attr">"must_not"</span>: [</span><br><span class="line">        &#123;<span class="attr">"match"</span>: &#123;</span><br><span class="line">          <span class="attr">"name"</span>: <span class="string">"阿莫西林"</span></span><br><span class="line">        &#125;&#125;</span><br><span class="line">      ]</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>以上结果得分为0：<br><img src="index_files/c7e02b62-d07c-435e-a3f1-d1cf601f61e0.png" alt=""></p><h4 id="Boosting-查询"><a href="#Boosting-查询" class="headerlink" title="Boosting 查询"></a>Boosting 查询</h4><blockquote><p>通过一定规则来影响得分，可以使用boosting查询来降级某些文档，而不必将它们从搜索结果中排除</p></blockquote><p>例：<br><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="attr">"query"</span>: &#123;</span><br><span class="line">        <span class="attr">"boosting"</span> : &#123;</span><br><span class="line">            <span class="attr">"positive"</span> : &#123;</span><br><span class="line">                <span class="attr">"term"</span> : &#123;</span><br><span class="line">                    <span class="attr">"text"</span> : <span class="string">"apple"</span></span><br><span class="line">                &#125;</span><br><span class="line">            &#125;,</span><br><span class="line">            <span class="attr">"negative"</span> : &#123;</span><br><span class="line">                 <span class="attr">"term"</span> : &#123;</span><br><span class="line">                     <span class="attr">"text"</span> : <span class="string">"pie"</span></span><br><span class="line">                &#125;</span><br><span class="line">            &#125;,</span><br><span class="line">            <span class="attr">"negative_boost"</span> : <span class="number">0.5</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><ul><li>positive(必需)：正向激励，查询出包含”apple”的数据。</li><li>negative(必需)：负向激励，降低包含”pie”的得分，但不会排除</li><li>negative_boost(必需)：降低得分的比重<blockquote><p>假如positive返回的数据得分为2，negative匹配后会将2*0.5 = 1，所以最终得分1，以此降低包含“pie”数据的得分</p></blockquote></li></ul><h4 id="Constant-score-查询（固定得分查询）"><a href="#Constant-score-查询（固定得分查询）" class="headerlink" title="Constant score 查询（固定得分查询）"></a>Constant score 查询（固定得分查询）</h4><blockquote><p>包装filter查询（filter的默认得分是0），给匹配的文档赋一个固定的得分</p></blockquote><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">&#123;</span><br><span class="line">    <span class="attr">"query"</span>: &#123;</span><br><span class="line">        <span class="attr">"constant_score"</span> : &#123;</span><br><span class="line">            <span class="attr">"filter"</span> : &#123;</span><br><span class="line">                <span class="attr">"term"</span> : &#123; <span class="attr">"user"</span> : <span class="string">"kimchy"</span>&#125;</span><br><span class="line">            &#125;,</span><br><span class="line">            <span class="attr">"boost"</span> : <span class="number">1.2</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>filter（必需）：用于过滤出文档</li><li>boost（可选，默认1.0）： 设置的固定得分</li></ul><h4 id="Disjunction-max-query-最佳匹配查询-or-分离最大化查询"><a href="#Disjunction-max-query-最佳匹配查询-or-分离最大化查询" class="headerlink" title="Disjunction max query(最佳匹配查询 or 分离最大化查询)"></a>Disjunction max query(最佳匹配查询 or 分离最大化查询)</h4><blockquote><p>将任何与任一查询匹配的文档作为结果返回，但只将最佳匹配的评分作为查询的评分结果返回,也就是返回最高的一个得分</p></blockquote><p>例：<br><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="attr">"query"</span>: &#123;</span><br><span class="line">        <span class="attr">"dis_max"</span> : &#123;</span><br><span class="line">            <span class="attr">"queries"</span> : [</span><br><span class="line">                &#123; <span class="attr">"term"</span> : &#123; <span class="attr">"title"</span> : <span class="string">"Quick pets"</span> &#125;&#125;,</span><br><span class="line">                &#123; <span class="attr">"term"</span> : &#123; <span class="attr">"body"</span> : <span class="string">"Quick pets"</span> &#125;&#125;</span><br><span class="line">            ],</span><br><span class="line">            <span class="attr">"tie_breaker"</span> : <span class="number">0.7</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><ul><li>queries（必需的查询对象数组）：包含一个或多个查询子句，并返回匹配分最高的一个得分</li><li>tie_breaker(可选)：值是一个浮点数（0~1.0），该参数的作用是将除最高分外其他几个条件的匹配得分也考虑进去。例如：值为0.2，那么 最终得分= 最高分 + 其他得分 * 0.2</li></ul><p>参考链接：<a href="https://www.elastic.co/guide/cn/elasticsearch/guide/current/_best_fields.html#_best_fields" target="_blank" rel="noopener">https://www.elastic.co/guide/cn/elasticsearch/guide/current/_best_fields.html#_best_fields</a></p><h4 id="Function-score-查询-（功能得分查询）"><a href="#Function-score-查询-（功能得分查询）" class="headerlink" title="Function score 查询 （功能得分查询）"></a>Function score 查询 （功能得分查询）</h4><blockquote><p>就是为返回的结果计算一个新的得分，包含一个查询和一个或多个函数，这些函数为每个返回的文档计算一个新得分。也能用过滤器对结果的<em>子集 </em>应用不同的函数</p></blockquote><p>例：<br><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="attr">"query"</span>: &#123;</span><br><span class="line">        <span class="attr">"function_score"</span>: &#123;</span><br><span class="line">            <span class="attr">"query"</span>: &#123; <span class="attr">"match"</span>: &#123;</span><br><span class="line">              <span class="attr">"name"</span>: <span class="string">"太极"</span></span><br><span class="line">            &#125; &#125;,</span><br><span class="line">            <span class="attr">"functions"</span>: [</span><br><span class="line">              &#123;</span><br><span class="line">                <span class="attr">"filter"</span>: &#123;<span class="attr">"match"</span>:&#123;<span class="attr">"name"</span>:<span class="string">"液"</span>&#125;&#125;,</span><br><span class="line">                <span class="attr">"random_score"</span>: &#123;&#125;</span><br><span class="line">              &#125;,</span><br><span class="line">              &#123;</span><br><span class="line">                <span class="attr">"filter"</span>: &#123;<span class="attr">"match"</span>:&#123;<span class="attr">"name"</span>:<span class="string">"通天"</span>&#125;&#125;,</span><br><span class="line">                <span class="attr">"weight"</span>: <span class="number">50</span></span><br><span class="line">              &#125;</span><br><span class="line">            ],</span><br><span class="line">            <span class="attr">"score_mode"</span>:<span class="string">"multiply"</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><blockquote><p>如果没有给函数提供过滤器，则等同于指定 “match_all”: {}</p></blockquote><ul><li>score_mode:指定如何组合计算出的分数</li></ul><table><thead><tr><th>取值</th><th>说明</th></tr></thead><tbody><tr><td>multiply</td><td>分数相乘（默认）</td></tr><tr><td>sum</td><td>分数相加</td></tr><tr><td>avg</td><td>分数求平均值</td></tr><tr><td>first</td><td>具有匹配过滤器的第一个函数被应用</td></tr><tr><td>max</td><td>使用最高分</td></tr><tr><td>min</td><td>使用最低分数</td></tr></tbody></table><blockquote><p>太多了，脑壳痛，参考链接：<a href="https://www.elastic.co/guide/en/elasticsearch/reference/7.3/query-dsl-function-score-query.html" target="_blank" rel="noopener">https://www.elastic.co/guide/en/elasticsearch/reference/7.3/query-dsl-function-score-query.html</a></p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;h3 id=&quot;复合查询&quot;&gt;&lt;a href=&quot;#复合查询&quot; class=&quot;headerlink&quot; title=&quot;复合查询&quot;&gt;&lt;/a&gt;复合查询&lt;/h3&gt;&lt;h4 id=&quot;bool-查询&quot;&gt;&lt;a href=&quot;#bool-查询&quot; class=&quot;headerlink&quot; title=&quot;bool 查询&quot;&gt;&lt;/a&gt;bool 查询&lt;/h4&gt;&lt;blockquote&gt;
&lt;p&gt;组合查询，组合多个查询类型&lt;/p&gt;
&lt;/blockquote&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;th&gt;计分&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;must&lt;/td&gt;
&lt;td&gt;字句必须出现在文档中&lt;/td&gt;
&lt;td&gt;是&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;filter&lt;/td&gt;
&lt;td&gt;字句必须匹配，如：status=1,在filter上下文中执行，不参与计分&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;should&lt;/td&gt;
&lt;td&gt;字句应该出现在文档中&lt;/td&gt;
&lt;td&gt;是&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;must_not&lt;/td&gt;
&lt;td&gt;字句不得出现在文档中，在filter上下文中执行&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
    
    </summary>
    
      <category term="ElasticSearch" scheme="http://blog.lyq3.com/categories/ElasticSearch/"/>
    
      <category term="DSL" scheme="http://blog.lyq3.com/categories/ElasticSearch/DSL/"/>
    
    
      <category term="ElasticSearch" scheme="http://blog.lyq3.com/tags/ElasticSearch/"/>
    
  </entry>
  
  <entry>
    <title>REST-Java High Level REST Client 文档管理 API</title>
    <link href="http://blog.lyq3.com/2019/10/12/elasticsearch/REST-Java%20High%20Level%20REST%20Client%20%E6%96%87%E6%A1%A3%E7%AE%A1%E7%90%86%20API/"/>
    <id>http://blog.lyq3.com/2019/10/12/elasticsearch/REST-Java High Level REST Client 文档管理 API/</id>
    <published>2019-10-12T12:43:28.000Z</published>
    <updated>2019-11-22T15:25:44.764Z</updated>
    
    <content type="html"><![CDATA[<h2 id="文档操作API"><a href="#文档操作API" class="headerlink" title="文档操作API"></a>文档操作API</h2><h4 id="添加文档数据"><a href="#添加文档数据" class="headerlink" title="添加文档数据"></a>添加文档数据</h4><ul><li>构建索引请求<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">IndexRequest request = <span class="keyword">new</span> IndexRequest(<span class="string">"posts"</span>);</span><br><span class="line">request.id(<span class="string">"1"</span>); <span class="comment">//文档ID</span></span><br><span class="line"><span class="comment">//数据</span></span><br><span class="line">String jsonString = </span><br><span class="line">    <span class="string">"&#123;"</span> + <span class="string">"\"user\":\"kimchy\","</span> + <span class="string">"\"postDate\":\"2013-01-30\","</span> + <span class="string">"\"message\":\"trying out Elasticsearch\""</span> + <span class="string">"&#125;"</span>; </span><br><span class="line">request.source(jsonString, XContentType.JSON);</span><br></pre></td></tr></table></figure></li></ul><a id="more"></a><blockquote><p>除了JSON字符串还可使用Map和XContentBuilder提供数据，另外还可使用 <code>Object</code> key-pairs 的方式：source(Object… source)</p></blockquote><ul><li>其他可选参数</li></ul><ol><li><p>路由设置</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">request.routing(<span class="string">"routing"</span>);<span class="comment">//指定分片</span></span><br></pre></td></tr></table></figure></li><li><p>超时时间设置</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//两种方式均可</span></span><br><span class="line"><span class="comment">//主分片达到可用状态的超时时间</span></span><br><span class="line">request.timeout(TimeValue.timeValueSeconds(<span class="number">1</span>));</span><br><span class="line">request.timeout(<span class="string">"1s"</span>);</span><br></pre></td></tr></table></figure></li><li><p>刷新策略</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//两种方式均可</span></span><br><span class="line"><span class="comment">//刷新策略不设置默认是NONE，不刷新</span></span><br><span class="line">request.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL); </span><br><span class="line">request.setRefreshPolicy(<span class="string">"wait_for"</span>);</span><br></pre></td></tr></table></figure></li></ol><blockquote><p>刷新策略参考：<a href="https://blog.csdn.net/hanchao5272/article/details/89151166" target="_blank" rel="noopener">https://blog.csdn.net/hanchao5272/article/details/89151166</a></p><ol start="4"><li>版本<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//常用于并发控制，比如两个人同时修改数据，如何保证自己的操作不会被覆盖？</span></span><br><span class="line">request.version(<span class="number">2</span>);</span><br></pre></td></tr></table></figure></li></ol></blockquote><ol start="5"><li><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//两种方式均可</span></span><br><span class="line"><span class="comment">//设置为CREATE则请求会创建数据，如果已存在则会报错</span></span><br><span class="line"><span class="comment">//默认属性是DocWriteRequest.OpType.INDEX,存在则更新，不存在则添加</span></span><br><span class="line">request.opType(DocWriteRequest.OpType.CREATE);</span><br><span class="line">request.opType(<span class="string">"create"</span>);</span><br></pre></td></tr></table></figure></li><li><p>设置管道名称</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">request.setPipeline(<span class="string">"pipeline"</span>);</span><br></pre></td></tr></table></figure></li></ol><ul><li><p>同步调用</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">IndexResponse indexResponse = client.index(request, RequestOptions.DEFAULT);</span><br></pre></td></tr></table></figure></li><li><p>异步调用<br>先构建监听器</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">listener = <span class="keyword">new</span> ActionListener&lt;IndexResponse&gt;() &#123;         <span class="meta">@Override</span> </span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onResponse</span><span class="params">(IndexResponse indexResponse)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//成功的处理逻辑</span></span><br><span class="line">    &#125; </span><br><span class="line">    <span class="meta">@Override</span> </span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onFailure</span><span class="params">(Exception e)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//失败的处理逻辑</span></span><br><span class="line">    &#125; </span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></li></ul><p>然后调用<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">client.indexAsync(request, RequestOptions.DEFAULT, listener);</span><br></pre></td></tr></table></figure></p><ul><li><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></pre></td><td class="code"><pre><span class="line">String index = indexResponse.getIndex();</span><br><span class="line">String id = indexResponse.getId();</span><br><span class="line"><span class="keyword">if</span> (indexResponse.getResult() == DocWriteResponse.Result.CREATED) &#123;</span><br><span class="line">    <span class="comment">//当数据是insert时的逻辑</span></span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span> (indexResponse.getResult() == DocWriteResponse.Result.UPDATED) &#123;</span><br><span class="line">    <span class="comment">//当数据是update时的逻辑</span></span><br><span class="line">&#125;</span><br><span class="line">ReplicationResponse.ShardInfo shardInfo = indexResponse.getShardInfo();</span><br><span class="line"><span class="keyword">if</span> (shardInfo.getTotal() != shardInfo.getSuccessful()) &#123;</span><br><span class="line">    <span class="comment">//当成功的分片数少于总分片数的情况处理</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> (shardInfo.getFailed() &gt; <span class="number">0</span>) &#123;</span><br><span class="line">    <span class="comment">//其他异常处理</span></span><br><span class="line">    <span class="keyword">for</span> (ReplicationResponse.ShardInfo.Failure failure :</span><br><span class="line">            shardInfo.getFailures()) &#123;</span><br><span class="line">        String reason = failure.reason(); </span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>存在版本冲突报错：ElasticsearchException</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">IndexRequest request = <span class="keyword">new</span> IndexRequest(<span class="string">"posts"</span>)</span><br><span class="line">    .id(<span class="string">"1"</span>)</span><br><span class="line">    .source(<span class="string">"field"</span>, <span class="string">"value"</span>)</span><br><span class="line">    .setIfSeqNo(<span class="number">10L</span>)</span><br><span class="line">    .setIfPrimaryTerm(<span class="number">20</span>);</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">    IndexResponse response = client.index(request, RequestOptions.DEFAULT);</span><br><span class="line">&#125; <span class="keyword">catch</span>(ElasticsearchException e) &#123;</span><br><span class="line">    <span class="keyword">if</span> (e.status() == RestStatus.CONFLICT) &#123;</span><br><span class="line">        </span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>版本冲突就会造成报异常，如设置request.opType(DocWriteRequest.OpType.CREATE);后添加了一条已存在的数据。或者设置了setIfSeqNo(10L)等操作</p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;文档操作API&quot;&gt;&lt;a href=&quot;#文档操作API&quot; class=&quot;headerlink&quot; title=&quot;文档操作API&quot;&gt;&lt;/a&gt;文档操作API&lt;/h2&gt;&lt;h4 id=&quot;添加文档数据&quot;&gt;&lt;a href=&quot;#添加文档数据&quot; class=&quot;headerlink&quot; title=&quot;添加文档数据&quot;&gt;&lt;/a&gt;添加文档数据&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;构建索引请求&lt;figure class=&quot;highlight java&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;IndexRequest request = &lt;span class=&quot;keyword&quot;&gt;new&lt;/span&gt; IndexRequest(&lt;span class=&quot;string&quot;&gt;&quot;posts&quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;request.id(&lt;span class=&quot;string&quot;&gt;&quot;1&quot;&lt;/span&gt;); &lt;span class=&quot;comment&quot;&gt;//文档ID&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;comment&quot;&gt;//数据&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;String jsonString = &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;string&quot;&gt;&quot;&amp;#123;&quot;&lt;/span&gt; + &lt;span class=&quot;string&quot;&gt;&quot;\&quot;user\&quot;:\&quot;kimchy\&quot;,&quot;&lt;/span&gt; + &lt;span class=&quot;string&quot;&gt;&quot;\&quot;postDate\&quot;:\&quot;2013-01-30\&quot;,&quot;&lt;/span&gt; + &lt;span class=&quot;string&quot;&gt;&quot;\&quot;message\&quot;:\&quot;trying out Elasticsearch\&quot;&quot;&lt;/span&gt; + &lt;span class=&quot;string&quot;&gt;&quot;&amp;#125;&quot;&lt;/span&gt;; &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;request.source(jsonString, XContentType.JSON);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;/ul&gt;
    
    </summary>
    
      <category term="ElasticSearch" scheme="http://blog.lyq3.com/categories/ElasticSearch/"/>
    
      <category term="RestClient" scheme="http://blog.lyq3.com/categories/ElasticSearch/RestClient/"/>
    
    
      <category term="ElasticSearch" scheme="http://blog.lyq3.com/tags/ElasticSearch/"/>
    
  </entry>
  
  <entry>
    <title>REST-Java High Level REST Client 索引管理 API</title>
    <link href="http://blog.lyq3.com/2019/10/11/elasticsearch/REST-Java%20High%20Level%20REST%20Client%20%E7%B4%A2%E5%BC%95%E7%AE%A1%E7%90%86%20API/"/>
    <id>http://blog.lyq3.com/2019/10/11/elasticsearch/REST-Java High Level REST Client 索引管理 API/</id>
    <published>2019-10-11T12:43:28.000Z</published>
    <updated>2019-11-22T15:25:55.268Z</updated>
    
    <content type="html"><![CDATA[<h2 id="创建索引"><a href="#创建索引" class="headerlink" title="创建索引"></a>创建索引</h2><h4 id="构建索引请求"><a href="#构建索引请求" class="headerlink" title="构建索引请求"></a>构建索引请求</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">CreateIndexRequest request = <span class="keyword">new</span> CreateIndexRequest(<span class="string">"product"</span>);</span><br></pre></td></tr></table></figure><ul><li><p>设置分片</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">request.settings(Settings.builder() .put(<span class="string">"index.number_of_shards"</span>, <span class="number">3</span>) .put(<span class="string">"index.number_of_replicas"</span>, <span class="number">2</span>) );</span><br></pre></td></tr></table></figure></li><li><p>设置mapping</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">request.mapping( <span class="string">"&#123;\n"</span> + <span class="string">" \"properties\": &#123;\n"</span> + <span class="string">" \"message\": &#123;\n"</span> + <span class="string">" \"type\": \"text\"\n"</span> + <span class="string">" &#125;\n"</span> + <span class="string">" &#125;\n"</span> + <span class="string">"&#125;"</span>, XContentType.JSON);</span><br></pre></td></tr></table></figure></li></ul><a id="more"></a><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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"># JSON 参考</span><br><span class="line">&#123;</span><br><span class="line">    <span class="attr">"properties"</span>: &#123;</span><br><span class="line">        <span class="attr">"user"</span>: &#123;</span><br><span class="line">              <span class="attr">"type"</span>: <span class="string">"text"</span>,</span><br><span class="line">              <span class="attr">"analyzer"</span>: <span class="string">"ik_max_word"</span>,</span><br><span class="line">              <span class="attr">"search_analyzer"</span>: <span class="string">"ik_smart"</span></span><br><span class="line">            &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>es新版本不再推荐使用Type,这里不再设置type,不设置默认type为：_doc</p></blockquote><blockquote><p>除了提供JSON字符串设置mapping外还提供了Map和XContentBuilder方式提供</p></blockquote><ul><li>索引别名<blockquote><p>在elasticsearch里面给index起一个aliases（别名）能非常优雅的解决两个索引无缝切换的问题。<br>比如电商的核心商品索引库，除了实时增量数据外，每天都要重建一遍索引，避免index里面的数据和db里面的数据不一致。全量索引重建完再切换</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">request.alias(<span class="keyword">new</span> Alias(<span class="string">"product_alias"</span>).filter(QueryBuilders.termQuery(<span class="string">"user"</span>, <span class="string">"kimchy"</span>)));</span><br></pre></td></tr></table></figure></blockquote></li></ul><blockquote><p>除了单独设置maping()和settings()外还可以调用source（）一次性设置。如下：<br><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">&#123;</span><br><span class="line">  <span class="attr">"mappings"</span>: &#123;</span><br><span class="line">      <span class="attr">"properties"</span>: &#123;</span><br><span class="line">        <span class="attr">"user"</span>: &#123;</span><br><span class="line">              <span class="attr">"type"</span>: <span class="string">"text"</span>,</span><br><span class="line">              <span class="attr">"analyzer"</span>: <span class="string">"ik_max_word"</span>,</span><br><span class="line">              <span class="attr">"search_analyzer"</span>: <span class="string">"ik_smart"</span></span><br><span class="line">            &#125;,</span><br><span class="line">        <span class="attr">"title"</span>: &#123;</span><br><span class="line">          <span class="attr">"type"</span>: <span class="string">"text"</span>,</span><br><span class="line">          <span class="attr">"analyzer"</span>: <span class="string">"ik_max_word"</span>,</span><br><span class="line">          <span class="attr">"search_analyzer"</span>: <span class="string">"ik_smart"</span></span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="attr">"desc"</span>: &#123;</span><br><span class="line">          <span class="attr">"type"</span>: <span class="string">"text"</span>,</span><br><span class="line">          <span class="attr">"analyzer"</span>: <span class="string">"ik_max_word"</span>,</span><br><span class="line">          <span class="attr">"search_analyzer"</span>: <span class="string">"ik_smart"</span></span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="attr">"settings"</span> : &#123;</span><br><span class="line">        <span class="attr">"index"</span> : &#123;</span><br><span class="line">            <span class="attr">"number_of_shards"</span> : <span class="number">2</span>, </span><br><span class="line">            <span class="attr">"number_of_replicas"</span> : <span class="number">2</span> </span><br><span class="line">        &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="attr">"aliases"</span>:&#123;</span><br><span class="line">      <span class="attr">"product_alias"</span>:&#123;&#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p></blockquote><h4 id="索引的其他可选参数"><a href="#索引的其他可选参数" class="headerlink" title="索引的其他可选参数"></a>索引的其他可选参数</h4><ul><li><p>设置索引创建的超时时间</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">request.setTimeout(TimeValue.timeValueMinutes(<span class="number">2</span>));</span><br></pre></td></tr></table></figure></li><li><p>设置连接到master（主节点）的超时时间</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">request.setMasterTimeout(TimeValue.timeValueMinutes(<span class="number">1</span>));</span><br></pre></td></tr></table></figure></li><li><p>创建索引API返回响应之前要等待的活动分片副本数</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">request.waitForActiveShards(ActiveShardCount.from(<span class="number">2</span>));</span><br></pre></td></tr></table></figure></li></ul><blockquote><p>索引请求返回前需要等待多少个分片写入成功</p></blockquote><h4 id="异步请求"><a href="#异步请求" class="headerlink" title="异步请求"></a>异步请求</h4><ul><li><p>构建listener监听请求</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">ActionListener&lt;CreateIndexResponse&gt; listener = </span><br><span class="line">    <span class="keyword">new</span> ActionListener&lt;CreateIndexResponse&gt;() &#123;</span><br><span class="line">    <span class="meta">@Override</span> </span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onResponse</span><span class="params">(CreateIndexResponse createIndexResponse)</span> </span>&#123; </span><br><span class="line">        <span class="comment">//请求成功的处理逻辑</span></span><br><span class="line">    &#125; </span><br><span class="line">    <span class="meta">@Override</span> </span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onFailure</span><span class="params">(Exception e)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//请求失败的处理逻辑</span></span><br><span class="line">    &#125; </span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></li><li><p>发送请求并监听</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">client.indices().createAsync(request, RequestOptions.DEFAULT, listener);</span><br></pre></td></tr></table></figure></li></ul><h4 id="接收处理响应"><a href="#接收处理响应" class="headerlink" title="接收处理响应"></a>接收处理响应</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//所以节点是否都成功接收请求</span></span><br><span class="line"><span class="keyword">boolean</span> acknowledged = createIndexResponse.isAcknowledged(); </span><br><span class="line"><span class="comment">//在超时之前是否为索引中的每个分片启动了必要数量的分片副本，副本数量就是上面设置的</span></span><br><span class="line"><span class="keyword">boolean</span> shardsAcknowledged = createIndexResponse.isShardsAcknowledged();</span><br></pre></td></tr></table></figure><p>参考链接：<a href="https://www.elastic.co/guide/en/elasticsearch/client/java-rest/7.3/java-rest-high-create-index.html" target="_blank" rel="noopener">https://www.elastic.co/guide/en/elasticsearch/client/java-rest/7.3/java-rest-high-create-index.html</a></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;h4 id=&quot;构建索引请求&quot;&gt;&lt;a href=&quot;#构建索引请求&quot; class=&quot;headerlink&quot; title=&quot;构建索引请求&quot;&gt;&lt;/a&gt;构建索引请求&lt;/h4&gt;&lt;figure class=&quot;highlight java&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;CreateIndexRequest request = &lt;span class=&quot;keyword&quot;&gt;new&lt;/span&gt; CreateIndexRequest(&lt;span class=&quot;string&quot;&gt;&quot;product&quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;设置分片&lt;/p&gt;
&lt;figure class=&quot;highlight java&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;request.settings(Settings.builder() .put(&lt;span class=&quot;string&quot;&gt;&quot;index.number_of_shards&quot;&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;3&lt;/span&gt;) .put(&lt;span class=&quot;string&quot;&gt;&quot;index.number_of_replicas&quot;&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;) );&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;设置mapping&lt;/p&gt;
&lt;figure class=&quot;highlight java&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;request.mapping( &lt;span class=&quot;string&quot;&gt;&quot;&amp;#123;\n&quot;&lt;/span&gt; + &lt;span class=&quot;string&quot;&gt;&quot; \&quot;properties\&quot;: &amp;#123;\n&quot;&lt;/span&gt; + &lt;span class=&quot;string&quot;&gt;&quot; \&quot;message\&quot;: &amp;#123;\n&quot;&lt;/span&gt; + &lt;span class=&quot;string&quot;&gt;&quot; \&quot;type\&quot;: \&quot;text\&quot;\n&quot;&lt;/span&gt; + &lt;span class=&quot;string&quot;&gt;&quot; &amp;#125;\n&quot;&lt;/span&gt; + &lt;span class=&quot;string&quot;&gt;&quot; &amp;#125;\n&quot;&lt;/span&gt; + &lt;span class=&quot;string&quot;&gt;&quot;&amp;#125;&quot;&lt;/span&gt;, XContentType.JSON);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;/ul&gt;
    
    </summary>
    
      <category term="ElasticSearch" scheme="http://blog.lyq3.com/categories/ElasticSearch/"/>
    
      <category term="RestClient" scheme="http://blog.lyq3.com/categories/ElasticSearch/RestClient/"/>
    
    
      <category term="ElasticSearch" scheme="http://blog.lyq3.com/tags/ElasticSearch/"/>
    
  </entry>
  
  <entry>
    <title>微服务学习之微服务基础</title>
    <link href="http://blog.lyq3.com/2018/04/29/SpringCloud/SpringCloud-core/"/>
    <id>http://blog.lyq3.com/2018/04/29/SpringCloud/SpringCloud-core/</id>
    <published>2018-04-29T10:11:28.000Z</published>
    <updated>2018-05-01T05:27:44.864Z</updated>
    
    <content type="html"><![CDATA[<h3 id="微服务基础"><a href="#微服务基础" class="headerlink" title="微服务基础"></a>微服务基础</h3><h4 id="1、-微服务带来的挑战"><a href="#1、-微服务带来的挑战" class="headerlink" title="1、 微服务带来的挑战"></a>1、 微服务带来的挑战</h4><ul><li>运维的新挑战<br>  微服务会导致部署的程序增多，合理的编排很重要</li><li>接口的一致性<br>  微服务虽然拆分了，但还是需要通过接口进行业务上的依赖，若接口改变了，需要提供方和调用方协调改动，不然会出现接口不一致问题。</li><li>分布式的复杂性<br>  由于拆分各个微服务都是独立运行各自的进程中，可能分布在不同的服务器、不同的地域，它们只能通过网络通信来进行合作，所以要考虑很多因素，如：网络延迟、分布式事务、异步消息、session同步等问题。</li></ul><h4 id="2、智能端口与哑管道"><a href="#2、智能端口与哑管道" class="headerlink" title="2、智能端口与哑管道"></a>2、智能端口与哑管道</h4><p>在单体应用中通过函数调用，在微服务中由于不在同一进程所以要通过网络调用，如果仅仅是改用RPC调用，会导致微服务之间产生繁琐的通信，使系统表现更为糟糕，所以需要更粗粒的的通信协议。</p><blockquote><p>在微服务中通常采用以下两种调用方式：</p></blockquote><ul><li>使用HTTP 的RESTful API 或轻量级消息发送协议</li><li>通过在轻量级消息总线上传递消息，如：rabbitMQ<a id="more"></a></li></ul><h4 id="3、去中心化"><a href="#3、去中心化" class="headerlink" title="3、去中心化"></a>3、去中心化</h4><p>采用微服务后，各个组件通过RESTful 接口进行调用依赖，各个应用组件可以使用自己的数据库，可以使用不同的数据库（如MongoDB），而且不用局限于某一种技术，各个组件都可以选择合适的技术栈，避免出现“杀鸡用牛刀”的局面。</p><h4 id="4、容错设计"><a href="#4、容错设计" class="headerlink" title="4、容错设计"></a>4、容错设计</h4><p>传统单体应用中，如果需要修改或增加一个功能则要将整个应用重新部署，如果某一组件出现问题就会出现一挂全挂的局面，而微服务架构中某一组件出现问题可以单独下线不影响其他功能使用。</p><h4 id="5、为什么选用SpringCloud"><a href="#5、为什么选用SpringCloud" class="headerlink" title="5、为什么选用SpringCloud"></a>5、为什么选用SpringCloud</h4><p>一句话：SpringCloud包含微服务的方方面面，大而全、成熟、省心、社区活跃、Spring和Netflix背书。</p><h4 id="6、SpringCloud简介"><a href="#6、SpringCloud简介" class="headerlink" title="6、SpringCloud简介"></a>6、SpringCloud简介</h4><p>SpringCloud包含多个子项目，还会不断增加：</p><ul><li>SpringCloud Config :配置中心，支持使用git存储配置文件，提高项目配置灵活性</li><li><p>SpringCloud Netflix: 核心组件，对Netflix OSS开源套件进行组合，包含：</p><ol><li>Eureka:注册中心，服务的发现与注册</li><li>Hystrix: 容错管理组件，提供降级熔断等保护机制</li><li>Ribbon： REST客户端调用组件，提供负载等功能</li><li>Feign: 基于 Ribbon： 和 Hystrix的声明式组件调用组件</li><li>Zuul: 网管组件，提供智能路由、访问过滤等功能</li><li>Archaius: 外部化配置组件</li></ol></li><li><p>SpringCloud Bus ： 事件、消息总线</p></li><li>SpringCloud Cluster: 针对ZooKeeper、Redis、Consul的选举算法和通用状态模式的实现<br>….</li></ul>]]></content>
    
    <summary type="html">
    
      &lt;h3 id=&quot;微服务基础&quot;&gt;&lt;a href=&quot;#微服务基础&quot; class=&quot;headerlink&quot; title=&quot;微服务基础&quot;&gt;&lt;/a&gt;微服务基础&lt;/h3&gt;&lt;h4 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;/h4&gt;&lt;ul&gt;
&lt;li&gt;运维的新挑战&lt;br&gt;  微服务会导致部署的程序增多，合理的编排很重要&lt;/li&gt;
&lt;li&gt;接口的一致性&lt;br&gt;  微服务虽然拆分了，但还是需要通过接口进行业务上的依赖，若接口改变了，需要提供方和调用方协调改动，不然会出现接口不一致问题。&lt;/li&gt;
&lt;li&gt;分布式的复杂性&lt;br&gt;  由于拆分各个微服务都是独立运行各自的进程中，可能分布在不同的服务器、不同的地域，它们只能通过网络通信来进行合作，所以要考虑很多因素，如：网络延迟、分布式事务、异步消息、session同步等问题。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;2、智能端口与哑管道&quot;&gt;&lt;a href=&quot;#2、智能端口与哑管道&quot; class=&quot;headerlink&quot; title=&quot;2、智能端口与哑管道&quot;&gt;&lt;/a&gt;2、智能端口与哑管道&lt;/h4&gt;&lt;p&gt;在单体应用中通过函数调用，在微服务中由于不在同一进程所以要通过网络调用，如果仅仅是改用RPC调用，会导致微服务之间产生繁琐的通信，使系统表现更为糟糕，所以需要更粗粒的的通信协议。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在微服务中通常采用以下两种调用方式：&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;使用HTTP 的RESTful API 或轻量级消息发送协议&lt;/li&gt;
&lt;li&gt;通过在轻量级消息总线上传递消息，如：rabbitMQ
    
    </summary>
    
      <category term="看书笔记" scheme="http://blog.lyq3.com/categories/%E7%9C%8B%E4%B9%A6%E7%AC%94%E8%AE%B0/"/>
    
      <category term="SpringCloud微服务实战" scheme="http://blog.lyq3.com/categories/%E7%9C%8B%E4%B9%A6%E7%AC%94%E8%AE%B0/SpringCloud%E5%BE%AE%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98/"/>
    
    
      <category term="SpringCloud" scheme="http://blog.lyq3.com/tags/SpringCloud/"/>
    
      <category term="微服务" scheme="http://blog.lyq3.com/tags/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"/>
    
  </entry>
  
  <entry>
    <title>Java基础之泛型总结</title>
    <link href="http://blog.lyq3.com/2017/11/07/JavaCore/java_genericity/"/>
    <id>http://blog.lyq3.com/2017/11/07/JavaCore/java_genericity/</id>
    <published>2017-11-07T14:19:58.000Z</published>
    <updated>2018-04-30T10:44:26.097Z</updated>
    
    <content type="html"><![CDATA[<h3 id="一、为什么使用泛型"><a href="#一、为什么使用泛型" class="headerlink" title="一、为什么使用泛型"></a>一、为什么使用泛型</h3><p>1.可读性好，一看泛型就知道里面装的什么类型的元素<br>2.泛型只在编译前存在，编译后会将类型擦除并替换成其限定类型，编译后就是一个普通类。泛型可以在编译前规范代码，防止放入一些不合格的对象导致运行期报错，提高程序健壮性。<br>3.使用泛型让程序更灵活，各类框架就大量使用泛型</p><blockquote><p>个人理解：泛型可以大概分为两部分：定义泛型和使用泛型，大多数时候我们是在使用泛型，也就是用具体类型限制，比如：List《String》,而写一些通用工具或者框架的时候需要自己定义泛型类，泛型方法等提高程序的灵活性。</p></blockquote><p><img src="/images/java_core/fx.jpg" alt="小小牛博客"></p><h3 id="二、定义简单泛型类"><a href="#二、定义简单泛型类" class="headerlink" title="二、定义简单泛型类"></a>二、定义简单泛型类</h3><p>例如：</p><h4 id="定义泛型类："><a href="#定义泛型类：" class="headerlink" title="定义泛型类："></a>定义泛型类：</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Pair</span>&lt;<span class="title">T</span>&gt;</span>&#123;</span><br><span class="line">    <span class="keyword">private</span> T first;</span><br><span class="line">    <span class="keyword">private</span> T second;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Pair</span><span class="params">()</span></span>&#123;first = <span class="keyword">null</span>;second = <span class="keyword">null</span>&#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">pair</span><span class="params">(T first, T second)</span></span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.first = first;</span><br><span class="line">        <span class="keyword">this</span>.second = second;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//set get 略...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><a id="more"></a><blockquote><p>类型变量使用大写形式，且比较短，在java库中，E表示集合的元素类型，K和V分别表示Key和值，T表示任意值，也可以使用邻近的U和S</p></blockquote><h4 id="使用泛型："><a href="#使用泛型：" class="headerlink" title="使用泛型："></a>使用泛型：</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Pair&lt;String&gt;</span><br></pre></td></tr></table></figure><h3 id="三、泛型方法"><a href="#三、泛型方法" class="headerlink" title="三、泛型方法"></a>三、泛型方法</h3><p>例：</p><h4 id="定义泛型方法"><a href="#定义泛型方法" class="headerlink" title="定义泛型方法"></a>定义泛型方法</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> ArrayAlg &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> &lt;T&gt; <span class="function">T <span class="title">getMiddle</span><span class="params">(T... a)</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> a[a.length / <span class="number">2</span>];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>泛型的类型变量放在修饰符的后面（这里是public static） ，返回值的前面，泛型方法可以定义在可以定义在普通类中，也可以定义在泛型类中</p></blockquote><h4 id="使用泛型方法"><a href="#使用泛型方法" class="headerlink" title="使用泛型方法"></a>使用泛型方法</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">String middle = ArrayAlg.&lt;String&gt;getMiddle(<span class="string">"aaa"</span>,<span class="string">"sss"</span>,<span class="string">"sdsd"</span>);</span><br></pre></td></tr></table></figure><p>上面限制了是String，如果放入一个int类型的参数就会报错，大多数情况下《String》可以省略的，编译器会自己推导出限制类型；<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ArrayAlg.getMiddle(<span class="string">"aaa"</span>,<span class="number">123</span>,<span class="string">"sdsd"</span>);</span><br></pre></td></tr></table></figure></p><p>上面这句代码不会报错，但是编译器推导的限制类型是String 和 Integer公共父类型，如果用String接收返回值会提示：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Incompatible types. Required String but <span class="string">'getMiddle'</span> was inferred to T: Incompatible types: Serializable &amp; Comparable&lt;? extends Serializable &amp; Comparable&lt;?&gt;&gt; is not convertible to String</span><br></pre></td></tr></table></figure><p>大概意思是可以赋值给Serializable 或 Comparable类型</p><h3 id="四、类型变量的限定"><a href="#四、类型变量的限定" class="headerlink" title="四、类型变量的限定"></a>四、类型变量的限定</h3><ul><li>我们在定义泛型的时候可以限定其范围：<br>如：<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> &lt;T extends Comparable&gt; <span class="function">T <span class="title">min</span><span class="params">(T[] a)</span>...</span></span><br></pre></td></tr></table></figure></li></ul><p>我们在使用时就只能使用实现了Comparable接口的类型来限制。</p><blockquote><p>这里是接口但依然使用extends关键字，主要是为了表明是父子关系。不管是类还是接口泛型里都是使用extends</p></blockquote><ul><li>一个类型变量或通配符可以有多个限定<br>如：<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">T extends Comparable &amp; Serializable</span><br></pre></td></tr></table></figure></li></ul><blockquote><p>可以根据需要拥有多个接口超类型，但是限定中至多有一个类，而且这个类必须在列表的第一个</p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">T extends <span class="class"><span class="keyword">class</span> &amp; <span class="title">interface</span> &amp; <span class="title">interface</span> &amp; <span class="title">interface</span> //就像这样的格式，如果有类，放在第一个</span></span><br></pre></td></tr></table></figure><h3 id="五、类型擦除"><a href="#五、类型擦除" class="headerlink" title="五、类型擦除"></a>五、类型擦除</h3><p>无论何时定义一个泛型类型，都自动提供一个原始类型，原始类型用第一个限定的类型变量来替换</p><p>T 是一个无限定的变量，所以会直接用Object替换，如 private T first 编译后变成 private Object first</p><p>如果是<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;T extends Comparable &amp; Serializable&gt;</span><br></pre></td></tr></table></figure></p><p>则原始类型会用第一个（即Comparable）代替，private Comparable first;</p><blockquote><p>在使用泛型的时候编译器会自动加入强制类型转换，转换成我们给定的类型</p></blockquote><p>如：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Pair&lt;String&gt; b = ...</span><br><span class="line">String ru = b.getName();</span><br></pre></td></tr></table></figure></p><p>类型擦除后getName返回类型会用Object替换T，编译器会插入String强制类型转换</p><blockquote><p>注意：要考虑类型擦除后带来的隐藏隐患</p></blockquote><h3 id="六、约束与局限性"><a href="#六、约束与局限性" class="headerlink" title="六、约束与局限性"></a>六、约束与局限性</h3><h4 id="1、不能用基本类型实例化类型参数"><a href="#1、不能用基本类型实例化类型参数" class="headerlink" title="1、不能用基本类型实例化类型参数"></a>1、不能用基本类型实例化类型参数</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">如：Pair&lt;<span class="keyword">int</span>&gt;不行，只能用Pair&lt;Integer&gt;</span><br></pre></td></tr></table></figure><h4 id="2、运行时类型查询只适用于原始类型"><a href="#2、运行时类型查询只适用于原始类型" class="headerlink" title="2、运行时类型查询只适用于原始类型"></a>2、运行时类型查询只适用于原始类型</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">例如：<span class="keyword">if</span>(a <span class="keyword">instanceof</span> Pair&lt;String&gt;) <span class="comment">//报错</span></span><br><span class="line">     ( Pair&lt;String&gt;) a <span class="comment">//强转，报错</span></span><br></pre></td></tr></table></figure><h4 id="3、不能创建参数化类型的数组"><a href="#3、不能创建参数化类型的数组" class="headerlink" title="3、不能创建参数化类型的数组"></a>3、不能创建参数化类型的数组</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Pair&lt;String&gt;[] table = <span class="keyword">new</span> Pair&lt;String&gt;[<span class="number">10</span>];<span class="comment">//报错，声明不会报错，但初始化就会报错</span></span><br></pre></td></tr></table></figure><h4 id="4、不能实例化类型变量"><a href="#4、不能实例化类型变量" class="headerlink" title="4、不能实例化类型变量"></a>4、不能实例化类型变量</h4><p>如：new T[]</p><h4 id="5、不能构造泛型数组"><a href="#5、不能构造泛型数组" class="headerlink" title="5、不能构造泛型数组"></a>5、不能构造泛型数组</h4><p>如：T[]</p><h4 id="6、泛型类的静态上下文中类型变量无效"><a href="#6、泛型类的静态上下文中类型变量无效" class="headerlink" title="6、泛型类的静态上下文中类型变量无效"></a>6、泛型类的静态上下文中类型变量无效</h4><p>如：private static T abc;//报错</p><h4 id="7、不能抛出或捕获泛型类的实例"><a href="#7、不能抛出或捕获泛型类的实例" class="headerlink" title="7、不能抛出或捕获泛型类的实例"></a>7、不能抛出或捕获泛型类的实例</h4><p>如：<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></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span>&#123;</span><br><span class="line"></span><br><span class="line">&#125;<span class="keyword">catch</span>(T e)&#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>不过，在异常规范中使用类型变量是允许的</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> &lt;T extends Throwable&gt; <span class="function"><span class="keyword">void</span> <span class="title">doWork</span><span class="params">(T t)</span> thows T</span>&#123;<span class="comment">//ok</span></span><br><span class="line">    <span class="keyword">try</span>&#123;</span><br><span class="line"></span><br><span class="line">    &#125;<span class="keyword">catch</span>(Throwable realCause)&#123;</span><br><span class="line">        t.initCause(realCause);</span><br><span class="line">        <span class="keyword">throw</span> t;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="8、注意泛型的继承"><a href="#8、注意泛型的继承" class="headerlink" title="8、注意泛型的继承"></a>8、注意泛型的继承</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"> A extends B</span><br><span class="line">Pair&lt;B&gt; 不是 Pair&lt;A&gt;  的子类，会类型擦除的</span><br></pre></td></tr></table></figure><h3 id="七、通配符类型"><a href="#七、通配符类型" class="headerlink" title="七、通配符类型"></a>七、通配符类型</h3><p>能一定程度解决上面泛型不能继承的问题<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Pair&lt;? extends B&gt;</span><br></pre></td></tr></table></figure></p><p>如果调用上面方法的set方法会报错，不能设置值，因为不能确定是什么类型的值，可以get</p><ul><li><p>? extends B 不能设置值，如果是集合（如List）就不能添加值。因为不能到是什么类型的值，但是能获取值，获取的值一定是B的子类，可以用B接收</p></li><li><p>？ super B 不能获取值，能设置/添加值，因为一定是B的子类，不能获取值，如果获取出来用什么接收呢？因为类型不确定。</p></li><li>？ 不能添加也不能获取，道理同上，但是能加入null，因为对象都能表示为null</li></ul><h3 id="八、反射和泛型"><a href="#八、反射和泛型" class="headerlink" title="八、反射和泛型"></a>八、反射和泛型</h3><blockquote><p>未完。。</p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;h3 id=&quot;一、为什么使用泛型&quot;&gt;&lt;a href=&quot;#一、为什么使用泛型&quot; class=&quot;headerlink&quot; title=&quot;一、为什么使用泛型&quot;&gt;&lt;/a&gt;一、为什么使用泛型&lt;/h3&gt;&lt;p&gt;1.可读性好，一看泛型就知道里面装的什么类型的元素&lt;br&gt;2.泛型只在编译前存在，编译后会将类型擦除并替换成其限定类型，编译后就是一个普通类。泛型可以在编译前规范代码，防止放入一些不合格的对象导致运行期报错，提高程序健壮性。&lt;br&gt;3.使用泛型让程序更灵活，各类框架就大量使用泛型&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;个人理解：泛型可以大概分为两部分：定义泛型和使用泛型，大多数时候我们是在使用泛型，也就是用具体类型限制，比如：List《String》,而写一些通用工具或者框架的时候需要自己定义泛型类，泛型方法等提高程序的灵活性。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;/images/java_core/fx.jpg&quot; alt=&quot;小小牛博客&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;二、定义简单泛型类&quot;&gt;&lt;a href=&quot;#二、定义简单泛型类&quot; class=&quot;headerlink&quot; title=&quot;二、定义简单泛型类&quot;&gt;&lt;/a&gt;二、定义简单泛型类&lt;/h3&gt;&lt;p&gt;例如：&lt;/p&gt;
&lt;h4 id=&quot;定义泛型类：&quot;&gt;&lt;a href=&quot;#定义泛型类：&quot; class=&quot;headerlink&quot; title=&quot;定义泛型类：&quot;&gt;&lt;/a&gt;定义泛型类：&lt;/h4&gt;&lt;figure class=&quot;highlight java&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;class&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;title&quot;&gt;Pair&lt;/span&gt;&amp;lt;&lt;span class=&quot;title&quot;&gt;T&lt;/span&gt;&amp;gt;&lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;private&lt;/span&gt; T first;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;private&lt;/span&gt; T second;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;function&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;title&quot;&gt;Pair&lt;/span&gt;&lt;span class=&quot;params&quot;&gt;()&lt;/span&gt;&lt;/span&gt;&amp;#123;first = &lt;span class=&quot;keyword&quot;&gt;null&lt;/span&gt;;second = &lt;span class=&quot;keyword&quot;&gt;null&lt;/span&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;function&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;title&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;params&quot;&gt;(T first, T second)&lt;/span&gt;&lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &lt;span class=&quot;keyword&quot;&gt;this&lt;/span&gt;.first = first;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &lt;span class=&quot;keyword&quot;&gt;this&lt;/span&gt;.second = second;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;comment&quot;&gt;//set get 略...&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
    
    </summary>
    
      <category term="看书笔记" scheme="http://blog.lyq3.com/categories/%E7%9C%8B%E4%B9%A6%E7%AC%94%E8%AE%B0/"/>
    
      <category term="Java核心技术卷(第10版)" scheme="http://blog.lyq3.com/categories/%E7%9C%8B%E4%B9%A6%E7%AC%94%E8%AE%B0/Java%E6%A0%B8%E5%BF%83%E6%8A%80%E6%9C%AF%E5%8D%B7-%E7%AC%AC10%E7%89%88/"/>
    
    
      <category term="JAVA" scheme="http://blog.lyq3.com/tags/JAVA/"/>
    
      <category term="泛型" scheme="http://blog.lyq3.com/tags/%E6%B3%9B%E5%9E%8B/"/>
    
  </entry>
  
  <entry>
    <title>Java8新特性之Lambda表达式总结</title>
    <link href="http://blog.lyq3.com/2017/10/31/JavaCore/Java8_lambda/"/>
    <id>http://blog.lyq3.com/2017/10/31/JavaCore/Java8_lambda/</id>
    <published>2017-10-31T13:19:58.000Z</published>
    <updated>2018-04-30T10:44:26.096Z</updated>
    
    <content type="html"><![CDATA[<h3 id="函数式接口"><a href="#函数式接口" class="headerlink" title="函数式接口"></a>函数式接口</h3><p>Lambda不是所有方法都能使用的，得满足一定的条件，比如得满足函数式接口。<br><img src="/images/java_core/lmbda.jpg" alt="小小牛博客"></p><blockquote><p>对于只有一个抽象方法的接口，需要这种借口的对象时，就可以提供一个Lambda表达式，这种接口称为函数式接口，也就是只有一个抽象方法的接口（不包括继承的）</p></blockquote><a id="more"></a><p>函数式接口其实本质上还是一个接口，但是它是一种特殊的接口：SAM类型的接口（Single Abstract Method）。定义了这种类型的接口，使得以其为参数的方法，可以在调用时，使用一个lambda表达式作为参数。从另一个方面说，一旦我们调用某方法，可以传入lambda表达式作为参数，则这个方法的参数类型，必定是一个函数式的接口，(这个类型可以使用@FunctionalInterface进行修饰，好处是，如果无意中增加了方法或者不符合lambda接口规范，编译器会提示错误，另外javadoc中会指出是一个函数式接口，推荐使用注解</p><p> 　　从SAM原则上讲，这个接口中，<strong> 只能有一个函数需要被实现 </strong>，但是也可以有如下例外:</p><p> 　　　　1. 默认方法与静态方法并不影响函数式接口的契约，可以任意使用，即</p><p> 　　　　　　函数式接口中可以有静态方法，一个或者多个静态方法不会影响SAM接口成为函数式接口，并且静态方法可以提供方法实现</p><p> 　　　　　　可以由 default 修饰的默认方法方法，这个关键字是Java8中新增的，为的目的就是使得某一些接口，原则上只有一个方法被实现，但是由于历史原因，不得不加入一些方法来兼容整个JDK中的API，所以就需要使用default关键字来定义这样的方法</p><h3 id="Lambda语法"><a href="#Lambda语法" class="headerlink" title="Lambda语法"></a>Lambda语法</h3><p> 一例胜千言</p> <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"> <span class="comment">// =====Lambda</span></span><br><span class="line"> <span class="comment">//这里省略list的构造</span></span><br><span class="line">List&lt;String&gt; names = ...;</span><br><span class="line">Collections.sort(names, (o1, o2) -&gt; o1.compareTo(o2));</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//===原始形态</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//这里省略list的构造</span></span><br><span class="line">List&lt;String&gt; names = ...;</span><br><span class="line">Collections.sort(names, <span class="keyword">new</span> Comparator&lt;String&gt;() &#123;</span><br><span class="line">  <span class="meta">@Override</span></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">compare</span><span class="params">(String o1, String o2)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> o1.compareTo(o2);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>完整一般语法：<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></pre></td><td class="code"><pre><span class="line">(Type1 param1, Type2 param2, ..., TypeN paramN) -&gt; &#123;</span><br><span class="line">  statment1;</span><br><span class="line">  statment2;</span><br><span class="line">  <span class="comment">//.............</span></span><br><span class="line">  <span class="keyword">return</span> statmentM;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><ol><li>参数类型省略–绝大多数情况，编译器都可以从上下文环境中推断出lambda表达式的参数类型。这样lambda表达式就变成了：</li></ol><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">(param1,param2, ..., paramN) -&gt; &#123;</span><br><span class="line">  statment1;</span><br><span class="line">  statment2;</span><br><span class="line">  <span class="comment">//.............</span></span><br><span class="line">  <span class="keyword">return</span> statmentM;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="2"><li>当lambda表达式的参数个数只有一个，可以省略小括号。lambda表达式简写为：</li></ol><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">param1 -&gt; &#123;</span><br><span class="line">  statment1;</span><br><span class="line">  statment2;</span><br><span class="line">  <span class="comment">//.............</span></span><br><span class="line">  <span class="keyword">return</span> statmentM;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="3"><li>当lambda表达式只包含一条语句时，可以省略大括号、return和语句结尾的分号。<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">param1 -&gt; statme</span><br></pre></td></tr></table></figure></li></ol><h3 id="方法的引用"><a href="#方法的引用" class="headerlink" title="方法的引用"></a>方法的引用</h3><p>个人理解：上面的Lambda有点像是实现匿名内部类的一个简化版本，我们需要传入代码以实现函数式接口内的那个抽象方法，方法引用有点像是引用其他已经实现好的方法。我们就不用再实现了（当然得看引用的方法满不满足需求）</p><h4 id="方法引用："><a href="#方法引用：" class="headerlink" title="方法引用："></a>方法引用：</h4><p>objectName::instanceMethod (对象.方法名)<br>ClassName::staticMethod  (类名.静态方法)<br>ClassName::instanceMethod</p><p>前两种方式类似，等同于把lambda表达式的参数直接当成instanceMethod|staticMethod的参数来调用。比如System.out::println等同于x-&gt;System.out.println(x)；Math::max等同于(x, y)-&gt;Math.max(x,y)。</p><p>最后一种方式（类名+非静态方法），等同于把lambda表达式的第一个参数当成instanceMethod的目标对象，其他剩余参数当成该方法的参数。比如String::toLowerCase等同于x-&gt;x.toLowerCase()。</p><h4 id="构造器引用"><a href="#构造器引用" class="headerlink" title="构造器引用"></a>构造器引用</h4><p>构造器引用语法如下：构造器引用和方法引用有点类似，只不过方法名为new,如：ClassName::new，把lambda表达式的参数当成ClassName构造器的参数 。例如BigDecimal::new等同于x-&gt;new BigDecimal(x)。</p><p>可以用数组类型建立构造器引用：<br>int[] :: new<br>他有一个参数即数组长度<br>等价于Lambda : x -&gt; new int[x]</p><h3 id="变量的作用域"><a href="#变量的作用域" class="headerlink" title="变量的作用域"></a>变量的作用域</h3><p>Lambda表达式也可访问外部的变量(参考内部类)</p><p>lambda表达式的三个重要组成部分：</p><ul><li>输入参数</li><li>可执行语句</li><li>自由变量的值，这是指非参数而且不在代码中定义的变量</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">String[] array = &#123;<span class="string">"a"</span>, <span class="string">"b"</span>, <span class="string">"c"</span>&#125;;</span><br><span class="line"><span class="keyword">for</span>(Integer i : Lists.newArrayList(<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>))&#123;</span><br><span class="line">  Stream.of(array).map(item -&gt; Strings.padEnd(item, i, <span class="string">'@'</span>)).forEach(System.out::println);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上面的这个例子中，map中的lambda表达式访问外部变量Integer i。</p><p><strong><em> 不过lambda表达式访问外部变量有一个非常重要的限制：变量不可变（java是值传递，也就是说基本类型值不能变，只是引用不可变，而不是真正的不可变)可以参考内部类引用外部变量也是必须用final声明，不过在Lambda中编译器会隐式的当做final处理，不加fina也可以 </em></strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">String[] array = &#123;<span class="string">"a"</span>, <span class="string">"b"</span>, <span class="string">"c"</span>&#125;;</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i&lt;<span class="number">4</span>; i++)&#123;</span><br><span class="line">  Stream.of(array).map(item -&gt; Strings.padEnd(item, i, <span class="string">'@'</span>)).forEach(System.out::println);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上面这个要报错</p><h3 id="Lambda中的this"><a href="#Lambda中的this" class="headerlink" title="Lambda中的this"></a>Lambda中的this</h3><p>在lambda中，this不是指向lambda表达式产生的那个SAM对象，而是声明它的外部对象。</p><h3 id="处理（接收）Lambda表达式"><a href="#处理（接收）Lambda表达式" class="headerlink" title="处理（接收）Lambda表达式"></a>处理（接收）Lambda表达式</h3><p>上面我们已经了解和使用了Lambda表达式，下面讨论下怎么接收处理Lambda表达式</p><p>使用Lambda的重点是延迟执行，像需要多次运行的场景就适合使用，jdk提供了很多常用的函数式接口，所以在自定义方法时可以使用这些函数式接口，不用自己再创建接口。还有一些基本类型的函数式接口，减少int double等的自动开装箱。</p>]]></content>
    
    <summary type="html">
    
      &lt;h3 id=&quot;函数式接口&quot;&gt;&lt;a href=&quot;#函数式接口&quot; class=&quot;headerlink&quot; title=&quot;函数式接口&quot;&gt;&lt;/a&gt;函数式接口&lt;/h3&gt;&lt;p&gt;Lambda不是所有方法都能使用的，得满足一定的条件，比如得满足函数式接口。&lt;br&gt;&lt;img src=&quot;/images/java_core/lmbda.jpg&quot; alt=&quot;小小牛博客&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;对于只有一个抽象方法的接口，需要这种借口的对象时，就可以提供一个Lambda表达式，这种接口称为函数式接口，也就是只有一个抽象方法的接口（不包括继承的）&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
      <category term="看书笔记" scheme="http://blog.lyq3.com/categories/%E7%9C%8B%E4%B9%A6%E7%AC%94%E8%AE%B0/"/>
    
      <category term="Java核心技术卷(第10版)" scheme="http://blog.lyq3.com/categories/%E7%9C%8B%E4%B9%A6%E7%AC%94%E8%AE%B0/Java%E6%A0%B8%E5%BF%83%E6%8A%80%E6%9C%AF%E5%8D%B7-%E7%AC%AC10%E7%89%88/"/>
    
    
      <category term="JAVA" scheme="http://blog.lyq3.com/tags/JAVA/"/>
    
      <category term="JAVA8" scheme="http://blog.lyq3.com/tags/JAVA8/"/>
    
      <category term="Lambda" scheme="http://blog.lyq3.com/tags/Lambda/"/>
    
  </entry>
  
  <entry>
    <title>Go语言学习之基础语法</title>
    <link href="http://blog.lyq3.com/2017/10/27/GoLang/base/"/>
    <id>http://blog.lyq3.com/2017/10/27/GoLang/base/</id>
    <published>2017-10-27T12:43:28.000Z</published>
    <updated>2019-11-18T14:01:17.793Z</updated>
    
    <content type="html"><![CDATA[<h3 id="包"><a href="#包" class="headerlink" title="包"></a>包</h3><p>Go使用package来组织代码。每一个可独立运行的Go程序，必定包含一个package main，在这个main包中必定包含一个入口函数main，而这个函数既没有参数，也没有返回值。Go使用UTF-8字符串和标识符(因为UTF-8的发明者也就是Go的发明者之一)，所以它天生支持多语言。<br><img src="/images/GoLang/go.jpg" alt="小小牛博客"></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h3 id=&quot;包&quot;&gt;&lt;a href=&quot;#包&quot; class=&quot;headerlink&quot; title=&quot;包&quot;&gt;&lt;/a&gt;包&lt;/h3&gt;&lt;p&gt;Go使用package来组织代码。每一个可独立运行的Go程序，必定包含一个package main，在这个main包中必定包含一个入口函数main，
      
    
    </summary>
    
      <category term="Go语言" scheme="http://blog.lyq3.com/categories/Go%E8%AF%AD%E8%A8%80/"/>
    
    
      <category term="Go语言" scheme="http://blog.lyq3.com/tags/Go%E8%AF%AD%E8%A8%80/"/>
    
  </entry>
  
  <entry>
    <title>【转】单点登录机制</title>
    <link href="http://blog.lyq3.com/2017/10/19/zhuan/sso_base/"/>
    <id>http://blog.lyq3.com/2017/10/19/zhuan/sso_base/</id>
    <published>2017-10-19T14:34:07.000Z</published>
    <updated>2018-04-30T10:44:26.101Z</updated>
    
    <content type="html"><![CDATA[<h3 id="一、单系统登录机制"><a href="#一、单系统登录机制" class="headerlink" title="一、单系统登录机制"></a>一、单系统登录机制</h3><h4 id="1、http无状态协议"><a href="#1、http无状态协议" class="headerlink" title="1、http无状态协议"></a>1、http无状态协议</h4><p>　　web应用采用browser/server架构，http作为通信协议。http是无状态协议，浏览器的每一次请求，服务器会独立处理，不与之前或之后的请求产生关联，这个过程用下图说明，三次请求/响应对之间没有任何联系</p><p>　　但这也同时意味着，任何用户都能通过浏览器访问服务器资源，如果想保护服务器的某些资源，必须限制浏览器请求；要限制浏览器请求，必须鉴别浏览器请求，响应合法请求，忽略非法请求；要鉴别浏览器请求，必须清楚浏览器请求状态。既然http协议无状态，那就让服务器和浏览器共同维护一个状态吧！这就是会话机制<br><a id="more"></a></p><h4 id="2、会话机制"><a href="#2、会话机制" class="headerlink" title="2、会话机制"></a>2、会话机制</h4><p>　　浏览器第一次请求服务器，服务器创建一个会话，并将会话的id作为响应的一部分发送给浏览器，浏览器存储会话id，并在后续第二次和第三次请求中带上会话id，服务器取得请求中的会话id就知道是不是同一个用户了，这个过程用下图说明，后续请求与第一次请求产生了关联</p><p>　　服务器在内存中保存会话对象，浏览器怎么保存会话id呢？你可能会想到两种方式</p><blockquote><p>1.请求参数<br>2.cookie</p></blockquote><p>　　将会话id作为每一个请求的参数，服务器接收请求自然能解析参数获得会话id，并借此判断是否来自同一会话，很明显，这种方式不靠谱。那就浏览器自己来维护这个会话id吧，每次发送http请求时浏览器自动发送会话id，cookie机制正好用来做这件事。cookie是浏览器用来存储少量数据的一种机制，数据以”key/value“形式存储，浏览器发送http请求时自动附带cookie信息</p><p>　　tomcat会话机制当然也实现了cookie，访问tomcat服务器时，浏览器中可以看到一个名为“JSESSIONID”的cookie，这就是tomcat会话机制维护的会话id，使用了cookie的请求响应过程如下图</p><h4 id="3、登录状态"><a href="#3、登录状态" class="headerlink" title="3、登录状态"></a>3、登录状态</h4><p>　　有了会话机制，登录状态就好明白了，我们假设浏览器第一次请求服务器需要输入用户名与密码验证身份，服务器拿到用户名密码去数据库比对，正确的话说明当前持有这个会话的用户是合法用户，应该将这个会话标记为“已授权”或者“已登录”等等之类的状态，既然是会话的状态，自然要保存在会话对象中，tomcat在会话对象中设置登录状态如下</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">HttpSession session = request.getSession();</span><br><span class="line">session.setAttribute(<span class="string">"isLogin"</span>, <span class="keyword">true</span>);</span><br></pre></td></tr></table></figure><p>　　用户再次访问时，tomcat在会话对象中查看登录状态<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">HttpSession session = request.getSession();</span><br><span class="line">session.getAttribute(<span class="string">"isLogin"</span>);</span><br></pre></td></tr></table></figure></p><p>　　每次请求受保护资源时都会检查会话对象中的登录状态，只有 isLogin=true 的会话才能访问，登录机制因此而实现。</p><h3 id="二、多系统的复杂性"><a href="#二、多系统的复杂性" class="headerlink" title="二、多系统的复杂性"></a>二、多系统的复杂性</h3><p>　　web系统早已从久远的单系统发展成为如今由多系统组成的应用群，面对如此众多的系统，用户难道要一个一个登录、然后一个一个注销吗？</p><p>　　web系统由单系统发展成多系统组成的应用群，复杂性应该由系统内部承担，而不是用户。无论web系统内部多么复杂，对用户而言，都是一个统一的整体，也就是说，用户访问web系统的整个应用群与访问单个系统一样，登录/注销只要一次就够了</p><p>　　虽然单系统的登录解决方案很完美，但对于多系统应用群已经不再适用了，为什么呢？</p><p>　　单系统登录解决方案的核心是cookie，cookie携带会话id在浏览器与服务器之间维护会话状态。但cookie是有限制的，这个限制就是cookie的域（通常对应网站的域名），浏览器发送http请求时会自动携带与该域匹配的cookie，而不是所有cookie</p><p>　　既然这样，为什么不将web应用群中所有子系统的域名统一在一个顶级域名下，例如” ☀.baidu.com “，然后将它们的cookie域设置为“baidu.com”，这种做法理论上是可以的，甚至早期很多多系统登录就采用这种同域名共享cookie的方式。</p><p>　　然而，可行并不代表好，共享cookie的方式存在众多局限。首先，应用群域名得统一；其次，应用群各系统使用的技术（至少是web服务器）要相同，不然cookie的key值（tomcat为JSESSIONID）不同，无法维持会话，共享cookie的方式是无法实现跨语言技术平台登录的，比如java、php、.net系统之间；第三，cookie本身不安全。</p><p>　　因此，我们需要一种全新的登录方式来实现多系统应用群的登录，这就是单点登录</p><h3 id="三、单点登录"><a href="#三、单点登录" class="headerlink" title="三、单点登录"></a>三、单点登录</h3><p>　　什么是单点登录？单点登录全称Single Sign On（以下简称SSO），是指在多系统应用群中登录一个系统，便可在其他所有系统中得到授权而无需再次登录，包括单点登录与单点注销两部分</p><h4 id="1、登录"><a href="#1、登录" class="headerlink" title="1、登录"></a>1、登录</h4><p>　　相比于单系统登录，sso需要一个独立的认证中心，只有认证中心能接受用户的用户名密码等安全信息，其他系统不提供登录入口，只接受认证中心的间接授权。间接授权通过令牌实现，sso认证中心验证用户的用户名密码没问题，创建授权令牌，在接下来的跳转过程中，授权令牌作为参数发送给各个子系统，子系统拿到令牌，即得到了授权，可以借此创建局部会话，局部会话登录方式与单系统的登录方式相同。这个过程，也就是单点登录的原理，用下图说明</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">用户访问系统<span class="number">1</span>的受保护资源，系统<span class="number">1</span>发现用户未登录，跳转至sso认证中心，并将自己的地址作为参数</span><br><span class="line">sso认证中心发现用户未登录，将用户引导至登录页面</span><br><span class="line">用户输入用户名密码提交登录申请</span><br><span class="line">sso认证中心校验用户信息，创建用户与sso认证中心之间的会话，称为全局会话，同时创建授权令牌</span><br><span class="line">sso认证中心带着令牌跳转会最初的请求地址（系统<span class="number">1</span>）</span><br><span class="line">系统<span class="number">1</span>拿到令牌，去sso认证中心校验令牌是否有效</span><br><span class="line">sso认证中心校验令牌，返回有效，注册系统<span class="number">1</span></span><br><span class="line">系统<span class="number">1</span>使用该令牌创建与用户的会话，称为局部会话，返回受保护资源</span><br><span class="line">用户访问系统<span class="number">2</span>的受保护资源</span><br><span class="line">系统<span class="number">2</span>发现用户未登录，跳转至sso认证中心，并将自己的地址作为参数</span><br><span class="line">sso认证中心发现用户已登录，跳转回系统<span class="number">2</span>的地址，并附上令牌</span><br><span class="line">系统<span class="number">2</span>拿到令牌，去sso认证中心校验令牌是否有效</span><br><span class="line">sso认证中心校验令牌，返回有效，注册系统<span class="number">2</span></span><br><span class="line">系统<span class="number">2</span>使用该令牌创建与用户的局部会话，返回受保护资源</span><br></pre></td></tr></table></figure><p>　　用户登录成功之后，会与sso认证中心及各个子系统建立会话，用户与sso认证中心建立的会话称为全局会话，用户与各个子系统建立的会话称为局部会话，局部会话建立之后，用户访问子系统受保护资源将不再通过sso认证中心，全局会话与局部会话有如下约束关系</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">局部会话存在，全局会话一定存在</span><br><span class="line">全局会话存在，局部会话不一定存在</span><br><span class="line">全局会话销毁，局部会话必须销毁</span><br></pre></td></tr></table></figure><p>　　你可以通过博客园、百度、csdn、淘宝等网站的登录过程加深对单点登录的理解，注意观察登录过程中的跳转url与参数</p><h4 id="2、注销"><a href="#2、注销" class="headerlink" title="2、注销"></a>2、注销</h4><p>　　单点登录自然也要单点注销，在一个子系统中注销，所有子系统的会话都将被销毁，用下面的图来说明</p><p>3b139d2e-0b83-4a69-b4f2-316adb8997ce</p><p>　　sso认证中心一直监听全局会话的状态，一旦全局会话销毁，监听器将通知所有注册系统执行注销操作</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">用户向系统<span class="number">1</span>发起注销请求</span><br><span class="line">系统<span class="number">1</span>根据用户与系统<span class="number">1</span>建立的会话id拿到令牌，向sso认证中心发起注销请求</span><br><span class="line">sso认证中心校验令牌有效，销毁全局会话，同时取出所有用此令牌注册的系统地址</span><br><span class="line">sso认证中心向所有注册系统发起注销请求</span><br><span class="line">各注册系统接收sso认证中心的注销请求，销毁局部会话</span><br><span class="line">sso认证中心引导用户至登录页面</span><br></pre></td></tr></table></figure><h3 id="四、部署图"><a href="#四、部署图" class="headerlink" title="四、部署图"></a>四、部署图</h3><p>　　单点登录涉及sso认证中心与众子系统，子系统与sso认证中心需要通信以交换令牌、校验令牌及发起注销请求，因而子系统必须集成sso的客户端，sso认证中心则是sso服务端，整个单点登录过程实质是sso客户端与服务端通信的过程</p><p>　　sso认证中心与sso客户端通信方式有多种，这里以简单好用的httpClient为例，web service、rpc、restful api都可以</p><h3 id="五、实现"><a href="#五、实现" class="headerlink" title="五、实现"></a>五、实现</h3><p>　　只是简要介绍下基于java的实现过程，不提供完整源码，明白了原理，我相信你们可以自己实现。sso采用客户端/服务端架构，我们先看sso-client与sso-server要实现的功能（下面：sso认证中心=sso-server）</p><p>　　sso-client</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">拦截子系统未登录用户请求，跳转至sso认证中心</span><br><span class="line">接收并存储sso认证中心发送的令牌</span><br><span class="line">与sso-server通信，校验令牌的有效性</span><br><span class="line">建立局部会话</span><br><span class="line">拦截用户注销请求，向sso认证中心发送注销请求</span><br><span class="line">接收sso认证中心发出的注销请求，销毁局部会话</span><br></pre></td></tr></table></figure><p>　　sso-server</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">验证用户的登录信息</span><br><span class="line">创建全局会话</span><br><span class="line">创建授权令牌</span><br><span class="line">与sso-client通信发送令牌</span><br><span class="line">校验sso-client令牌有效性</span><br><span class="line">系统注册</span><br><span class="line">接收sso-client注销请求，注销所有会话</span><br></pre></td></tr></table></figure><p>　　接下来，我们按照原理来一步步实现sso吧！</p><h4 id="1、sso-client拦截未登录请求"><a href="#1、sso-client拦截未登录请求" class="headerlink" title="1、sso-client拦截未登录请求"></a>1、sso-client拦截未登录请求</h4><p>　　java拦截请求的方式有servlet、filter、listener三种方式，我们采用filter。在sso-client中新建LoginFilter.java类并实现Filter接口，在doFilter()方法中加入对未登录用户的拦截</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">doFilter</span><span class="params">(ServletRequest request, ServletResponse response, FilterChain chain)</span> <span class="keyword">throws</span> IOException, ServletException </span>&#123;</span><br><span class="line">    HttpServletRequest req = (HttpServletRequest) request;</span><br><span class="line">    HttpServletResponse res = (HttpServletResponse) response;</span><br><span class="line">    HttpSession session = req.getSession();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (session.getAttribute(<span class="string">"isLogin"</span>)) &#123;</span><br><span class="line">        chain.doFilter(request, response);</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//跳转至sso认证中心</span></span><br><span class="line">    res.sendRedirect(<span class="string">"sso-server-url-with-system-url"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="2、sso-server拦截未登录请求"><a href="#2、sso-server拦截未登录请求" class="headerlink" title="2、sso-server拦截未登录请求"></a>2、sso-server拦截未登录请求</h4><p>　　拦截从sso-client跳转至sso认证中心的未登录请求，跳转至登录页面，这个过程与sso-client完全一样</p><h4 id="3、sso-server验证用户登录信息"><a href="#3、sso-server验证用户登录信息" class="headerlink" title="3、sso-server验证用户登录信息"></a>3、sso-server验证用户登录信息</h4><p>　　用户在登录页面输入用户名密码，请求登录，sso认证中心校验用户信息，校验成功，将会话状态标记为“已登录”</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@RequestMapping</span>(<span class="string">"/login"</span>)</span><br><span class="line"><span class="function"><span class="keyword">public</span> String <span class="title">login</span><span class="params">(String username, String password, HttpServletRequest req)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.checkLoginInfo(username, password);</span><br><span class="line">    req.getSession().setAttribute(<span class="string">"isLogin"</span>, <span class="keyword">true</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="string">"success"</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="4、sso-server创建授权令牌"><a href="#4、sso-server创建授权令牌" class="headerlink" title="4、sso-server创建授权令牌"></a>4、sso-server创建授权令牌</h4><p>　　授权令牌是一串随机字符，以什么样的方式生成都没有关系，只要不重复、不易伪造即可，下面是一个例子</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">String token = UUID.randomUUID().toString();</span><br></pre></td></tr></table></figure><h4 id="5、sso-client取得令牌并校验"><a href="#5、sso-client取得令牌并校验" class="headerlink" title="5、sso-client取得令牌并校验"></a>5、sso-client取得令牌并校验</h4><p>　　sso认证中心登录后，跳转回子系统并附上令牌，子系统（sso-client）取得令牌，然后去sso认证中心校验，在LoginFilter.java的doFilter()中添加几行</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 请求附带token参数</span></span><br><span class="line">String token = req.getParameter(<span class="string">"token"</span>);</span><br><span class="line"><span class="keyword">if</span> (token != <span class="keyword">null</span>) &#123;</span><br><span class="line">    <span class="comment">// 去sso认证中心校验token</span></span><br><span class="line">    <span class="keyword">boolean</span> verifyResult = <span class="keyword">this</span>.verify(<span class="string">"sso-server-verify-url"</span>, token);</span><br><span class="line">    <span class="keyword">if</span> (!verifyResult) &#123;</span><br><span class="line">        res.sendRedirect(<span class="string">"sso-server-url"</span>);</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    chain.doFilter(request, response);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>　　verify()方法使用httpClient实现，这里仅简略介绍，httpClient详细使用方法请参考官方文档</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">HttpPost httpPost = <span class="keyword">new</span> HttpPost(<span class="string">"sso-server-verify-url-with-token"</span>);</span><br><span class="line">HttpResponse httpResponse = httpClient.execute(httpPost);</span><br></pre></td></tr></table></figure><h4 id="6、sso-server接收并处理校验令牌请求"><a href="#6、sso-server接收并处理校验令牌请求" class="headerlink" title="6、sso-server接收并处理校验令牌请求"></a>6、sso-server接收并处理校验令牌请求</h4><p>　　用户在sso认证中心登录成功后，sso-server创建授权令牌并存储该令牌，所以，sso-server对令牌的校验就是去查找这个令牌是否存在以及是否过期，令牌校验成功后sso-server将发送校验请求的系统注册到sso认证中心（就是存储起来的意思）</p><p>　　令牌与注册系统地址通常存储在key-value数据库（如redis）中，redis可以为key设置有效时间也就是令牌的有效期。redis运行在内存中，速度非常快，正好sso-server不需要持久化任何数据。</p><p>　　令牌与注册系统地址可以用下图描述的结构存储在redis中，可能你会问，为什么要存储这些系统的地址？如果不存储，注销的时候就麻烦了，用户向sso认证中心提交注销请求，sso认证中心注销全局会话，但不知道哪些系统用此全局会话建立了自己的局部会话，也不知道要向哪些子系统发送注销请求注销局部会话</p><h4 id="7、sso-client校验令牌成功创建局部会话"><a href="#7、sso-client校验令牌成功创建局部会话" class="headerlink" title="7、sso-client校验令牌成功创建局部会话"></a>7、sso-client校验令牌成功创建局部会话</h4><p>　　令牌校验成功后，sso-client将当前局部会话标记为“已登录”，修改LoginFilter.java，添加几行</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (verifyResult) &#123;</span><br><span class="line">    session.setAttribute(<span class="string">"isLogin"</span>, <span class="keyword">true</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>　　sso-client还需将当前会话id与令牌绑定，表示这个会话的登录状态与令牌相关，此关系可以用java的hashmap保存，保存的数据用来处理sso认证中心发来的注销请求</p><h4 id="8、注销过程"><a href="#8、注销过程" class="headerlink" title="8、注销过程"></a>8、注销过程</h4><p>　　用户向子系统发送带有“logout”参数的请求（注销请求），sso-client拦截器拦截该请求，向sso认证中心发起注销请求</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">String logout = req.getParameter(<span class="string">"logout"</span>);</span><br><span class="line"><span class="keyword">if</span> (logout != <span class="keyword">null</span>) &#123;</span><br><span class="line">    <span class="keyword">this</span>.ssoServer.logout(token);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>　　sso认证中心也用同样的方式识别出sso-client的请求是注销请求（带有“logout”参数），sso认证中心注销全局会话</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@RequestMapping</span>(<span class="string">"/logout"</span>)</span><br><span class="line"><span class="function"><span class="keyword">public</span> String <span class="title">logout</span><span class="params">(HttpServletRequest req)</span> </span>&#123;</span><br><span class="line">    HttpSession session = req.getSession();</span><br><span class="line">    <span class="keyword">if</span> (session != <span class="keyword">null</span>) &#123;</span><br><span class="line">        session.invalidate();<span class="comment">//触发LogoutListener</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">"redirect:/"</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>　　sso认证中心有一个全局会话的监听器，一旦全局会话注销，将通知所有注册系统注销</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">LogoutListener</span> <span class="keyword">implements</span> <span class="title">HttpSessionListener</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">sessionCreated</span><span class="params">(HttpSessionEvent event)</span> </span>&#123;&#125;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">sessionDestroyed</span><span class="params">(HttpSessionEvent event)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//通过httpClient向所有注册系统发送注销请求</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      &lt;h3 id=&quot;一、单系统登录机制&quot;&gt;&lt;a href=&quot;#一、单系统登录机制&quot; class=&quot;headerlink&quot; title=&quot;一、单系统登录机制&quot;&gt;&lt;/a&gt;一、单系统登录机制&lt;/h3&gt;&lt;h4 id=&quot;1、http无状态协议&quot;&gt;&lt;a href=&quot;#1、http无状态协议&quot; class=&quot;headerlink&quot; title=&quot;1、http无状态协议&quot;&gt;&lt;/a&gt;1、http无状态协议&lt;/h4&gt;&lt;p&gt;　　web应用采用browser/server架构，http作为通信协议。http是无状态协议，浏览器的每一次请求，服务器会独立处理，不与之前或之后的请求产生关联，这个过程用下图说明，三次请求/响应对之间没有任何联系&lt;/p&gt;
&lt;p&gt;　　但这也同时意味着，任何用户都能通过浏览器访问服务器资源，如果想保护服务器的某些资源，必须限制浏览器请求；要限制浏览器请求，必须鉴别浏览器请求，响应合法请求，忽略非法请求；要鉴别浏览器请求，必须清楚浏览器请求状态。既然http协议无状态，那就让服务器和浏览器共同维护一个状态吧！这就是会话机制&lt;br&gt;
    
    </summary>
    
      <category term="网络转载" scheme="http://blog.lyq3.com/categories/%E7%BD%91%E7%BB%9C%E8%BD%AC%E8%BD%BD/"/>
    
    
      <category term="JAVA" scheme="http://blog.lyq3.com/tags/JAVA/"/>
    
      <category term="SSO" scheme="http://blog.lyq3.com/tags/SSO/"/>
    
      <category term="学习" scheme="http://blog.lyq3.com/tags/%E5%AD%A6%E4%B9%A0/"/>
    
  </entry>
  
  <entry>
    <title>SpringMVC异常处理，全局异常和Controller通知</title>
    <link href="http://blog.lyq3.com/2017/09/16/Spring_in_Action/SpringException/"/>
    <id>http://blog.lyq3.com/2017/09/16/Spring_in_Action/SpringException/</id>
    <published>2017-09-16T14:19:58.000Z</published>
    <updated>2018-04-30T10:44:26.098Z</updated>
    
    <content type="html"><![CDATA[<h3 id="1-Spring中的异常"><a href="#1-Spring中的异常" class="headerlink" title="1.Spring中的异常"></a>1.Spring中的异常</h3><p>首先，要对异常有一个清晰的认识，刚学JAVA那会儿对异常的了解很是朦胧，只知道try catch 可以捕获异常，可以输出异常。打印出异常信息，对何时应该捕获，何时抛异常并不在乎。知道上班后才渐渐的对异常处理重视起来。<br>一个程序不可能不出异常情况，关键在于我们能尽可能的预见将要发生的异常并提前对可能发生的异常进行处理，提高程序的健壮性。（PS:今天看了猩球崛起3，有点失望）<br><img src="/images/Spring_in_Action/xqjq.jpg" alt="小小牛博客"><br>Spring提供了多种方式将异常转换为响应：</p><ul><li>特定的Spring异常将会自动映射为指定的HTTP状态码；</li><li>异常上可以添加@ResponseStatus注解，从而将其映射为某一个HTTP状态码；</li><li>在方法上可以添加@ExceptionHandler注解，使其用来处理异常</li></ul><a id="more"></a><h3 id="2-将异常映射为HTTP状态码"><a href="#2-将异常映射为HTTP状态码" class="headerlink" title="2.将异常映射为HTTP状态码"></a>2.将异常映射为HTTP状态码</h3><table><thead><tr><th>Spring异常</th><th style="text-align:left">HTTP状态码</th></tr></thead><tbody><tr><td>BindException</td><td style="text-align:left">400 - Bad Request</td></tr><tr><td>ConversionNotSupportedException</td><td style="text-align:left">500 - Internal Server Error</td></tr><tr><td>HttpMediaTypeNotAcceptableException</td><td style="text-align:left">406 - Not Acceptable</td></tr><tr><td>HttpMediaTypeNotSupportedException</td><td style="text-align:left">415 - Unsupported Media Type</td></tr><tr><td>HttpMessageNotReadableException</td><td style="text-align:left">400 - Bad Request</td></tr><tr><td>HttpMessageNotWritableException</td><td style="text-align:left">500 - Internal Server Error</td></tr><tr><td>HttpRequestMethodNotSupportedException</td><td style="text-align:left">405 - Method Not Allowed</td></tr><tr><td>MethodArgumentNotValidException</td><td style="text-align:left">400 - Bad Request</td></tr><tr><td>MissingServletRequestParameterException</td><td style="text-align:left">400 - Bad Request</td></tr><tr><td>MissingServletRequestPartException</td><td style="text-align:left">400 - Bad Request</td></tr><tr><td>NoSuchRequestHandlingMethodException</td><td style="text-align:left">404 - Not Found</td></tr><tr><td>TypeMismatchException</td><td style="text-align:left">400 - Bad Reque</td></tr></tbody></table><p>以上这些状态码是Spring默认使用的异常及对应状态码，我们也可自己映射到状态码用@ResponseStatus注解<br>例如：<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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> org.springframework.http.HttpStatus;</span><br><span class="line"><span class="keyword">import</span> org.springframework.web.bind.annotation.ResponseStatus;</span><br><span class="line"></span><br><span class="line"><span class="meta">@ResponseStatus</span>(value=HttpStatus.NOT_FOUND<span class="comment">/*404*/</span>, reason=<span class="string">"找不到页面"</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SpittleNotFoundException</span> <span class="keyword">extends</span> <span class="title">RuntimeException</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h3 id="3-编写异常处理方法"><a href="#3-编写异常处理方法" class="headerlink" title="3.编写异常处理方法"></a>3.编写异常处理方法</h3><blockquote><p>将异常映射为状态码是一个简单易用的处理方案，但是在实际开放中我们除了要返回状态码还要返回错误信息和其他一些参数，上面这种方案显然不够用。所以我们得捕获异常并返回错误信息</p></blockquote><p>最常见的方式是，在每个请求的方法里捕获异常并返回相应的错误信息，JSON数据或者跳转到错误页面</p><p>也可以在每个Controller里写一个公用的方法来统一处理异常，并用@ExceptionHandler标注，这样，当有@RequestMapping的方法发生异常时，@ExceptionHandler标注的方法就会捕捉到你指定的异常并执行方法，<br>当然，也可以将这个异常方法写到一个BaseController里，其他Controller继承这个BaseController，甚至可以写一个全局的异常处理类，可以根据具体业务采取具体的方案</p><h3 id="4-ControllerAdvice为控制器添加通知"><a href="#4-ControllerAdvice为控制器添加通知" class="headerlink" title="4.@ControllerAdvice为控制器添加通知"></a>4.@ControllerAdvice为控制器添加通知</h3><p>控制器通知（controller advice）是任意带有@ControllerAdvice注解的类，这个类会包含一个或多个如下类型的方法：</p><blockquote><p>来一个个看：</p></blockquote><h4 id="ExceptionHandler注解标注的方法；"><a href="#ExceptionHandler注解标注的方法；" class="headerlink" title="@ExceptionHandler注解标注的方法；"></a>@ExceptionHandler注解标注的方法；</h4><p>有@RequestMapping的方法发生异常时会执行;</p><h4 id="InitBinder注解标注的方法；"><a href="#InitBinder注解标注的方法；" class="headerlink" title="@InitBinder注解标注的方法；"></a>@InitBinder注解标注的方法；</h4><blockquote><p>前台传过来的参数都是String类型的，有些参数不能自动转换，比如Date，需要使用@initBinder注解为binder提供一个数据的转换器，这个转换器可以自己实现，也可以用spring官方的一些实现。</p></blockquote><p>写个Double类型转换器<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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> org.springframework.beans.propertyeditors.PropertiesEditor;  </span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DoubleEditor</span> <span class="keyword">extends</span> <span class="title">PropertiesEditor</span> </span>&#123;    </span><br><span class="line">    <span class="meta">@Override</span>    </span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setAsText</span><span class="params">(String text)</span> <span class="keyword">throws</span> IllegalArgumentException </span>&#123;    </span><br><span class="line">        <span class="keyword">if</span> (text == <span class="keyword">null</span> || text.equals(<span class="string">""</span>)) &#123;    </span><br><span class="line">            text = <span class="string">"0"</span>;    </span><br><span class="line">        &#125;    </span><br><span class="line">        setValue(Double.parseDouble(text));    </span><br><span class="line">    &#125;    </span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span>    </span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getAsText</span><span class="params">()</span> </span>&#123;    </span><br><span class="line">        <span class="keyword">return</span> getValue().toString();    </span><br><span class="line">    &#125;    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>然后用@InitBinder注册</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@InitBinder</span>    </span><br><span class="line">   <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">initBinder</span><span class="params">(WebDataBinder binder)</span> </span>&#123;    </span><br><span class="line">       binder.registerCustomEditor(Date.class, <span class="keyword">new</span> CustomDateEditor(<span class="keyword">new</span> SimpleDateFormat(<span class="string">"yyyy-MM-dd HH:mm:ss"</span>), <span class="keyword">true</span>));<span class="comment">//时间转换  </span></span><br><span class="line">       binder.registerCustomEditor(<span class="keyword">double</span>.class, <span class="keyword">new</span> DoubleEditor());   <span class="comment">//Double转换</span></span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><h4 id="ModelAttribute注解标注的方法。"><a href="#ModelAttribute注解标注的方法。" class="headerlink" title="@ModelAttribute注解标注的方法。"></a>@ModelAttribute注解标注的方法。</h4><p>被@ModelAttribute注释的方法会在此controller每个方法执行前被执行。<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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Controller</span>  </span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">HelloModelController</span> </span>&#123;  </span><br><span class="line"></span><br><span class="line">    <span class="meta">@ModelAttribute</span>   </span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">populateModel</span><span class="params">(@RequestParam String abc, Model model)</span> </span>&#123;    </span><br><span class="line">       model.addAttribute(<span class="string">"attributeName"</span>, abc);    </span><br><span class="line">    &#125;    </span><br><span class="line"></span><br><span class="line">    <span class="meta">@RequestMapping</span>(value = <span class="string">"/helloWorld"</span>)    </span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">helloWorld</span><span class="params">()</span> </span>&#123;    </span><br><span class="line">       <span class="keyword">return</span> <span class="string">"helloWorld.jsp"</span>;    </span><br><span class="line">    &#125;    </span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>访问控制器方法helloWorld时，会首先调用populateModel方法，将页面参数abc(/helloWorld.ht?abc=text)放到model的attributeName属性中，在视图中可以直接访问。<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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Controller</span>  </span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Hello2ModelController</span> </span>&#123;  </span><br><span class="line"></span><br><span class="line">    <span class="meta">@ModelAttribute</span>   </span><br><span class="line">    <span class="function"><span class="keyword">public</span> User <span class="title">populateModel</span><span class="params">()</span> </span>&#123;    </span><br><span class="line">       User user=<span class="keyword">new</span> User();  </span><br><span class="line">       user.setAccount(<span class="string">"ray"</span>);  </span><br><span class="line">       <span class="keyword">return</span> user;  </span><br><span class="line">    &#125;    </span><br><span class="line">    <span class="meta">@RequestMapping</span>(value = <span class="string">"/helloWorld2"</span>)    </span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">helloWorld</span><span class="params">()</span> </span>&#123;    </span><br><span class="line">       <span class="keyword">return</span> <span class="string">"helloWorld.jsp"</span>;    </span><br><span class="line">    &#125;    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>前台可以通过user获取参数，如：${user.account}</p><blockquote><p>在带有@ControllerAdvice注解的类中，以上所述的这些方法会运用到整个应用程序所有控制器中带有@RequestMapping注解的方法上。</p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@ControllerAdvice</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AdviceHandler</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 全局异常处理</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@ExceptionHandler</span>(&#123; UnauthenticatedException.class, AuthenticationException.class &#125;)</span><br><span class="line">    <span class="meta">@ResponseBody</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> ResponseData <span class="title">authenticationException</span><span class="params">(HttpServletRequest request, HttpServletResponse response)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 输出JSON</span></span><br><span class="line">        ResponseData res = <span class="keyword">new</span> ResponseData();</span><br><span class="line">        res.setCode(<span class="number">888</span>).setMsg(<span class="string">"未登录"</span>);</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">    *全局转换器</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">    <span class="meta">@InitBinder</span>    </span><br><span class="line">     <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">initBinder</span><span class="params">(WebDataBinder binder)</span> </span>&#123;    </span><br><span class="line">         binder.registerCustomEditor(Date.class, <span class="keyword">new</span> CustomDateEditor(<span class="keyword">new</span> SimpleDateFormat(<span class="string">"yyyy-MM-dd HH:mm:ss"</span>), <span class="keyword">true</span>));<span class="comment">//时间转换  </span></span><br><span class="line">         binder.registerCustomEditor(<span class="keyword">double</span>.class, <span class="keyword">new</span> DoubleEditor());   <span class="comment">//Double转换</span></span><br><span class="line">     &#125;</span><br><span class="line">     <span class="comment">/**</span></span><br><span class="line"><span class="comment">     *全局返回数据</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">     <span class="meta">@ModelAttribute</span>   </span><br><span class="line">     <span class="function"><span class="keyword">public</span> User <span class="title">populateModel</span><span class="params">()</span> </span>&#123;    </span><br><span class="line">        User user=<span class="keyword">new</span> User();  </span><br><span class="line">        user.setAccount(<span class="string">"ray"</span>);  </span><br><span class="line">        <span class="keyword">return</span> user;  </span><br><span class="line">     &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="5-URL重定向传参"><a href="#5-URL重定向传参" class="headerlink" title="5.URL重定向传参"></a>5.URL重定向传参</h3><h4 id="通过URL传参"><a href="#通过URL传参" class="headerlink" title="通过URL传参"></a>通过URL传参</h4><p><img src="/images/Spring_in_Action/parm1.png" alt="小小牛博客"></p><p>如果username属性的值是habuma并且spitterId属性的值是42，<br>那么结果得到的重定向URL路径将会是“/spitter/habuma?<br>spitterId=42”。</p><h4 id="使用flash属性"><a href="#使用flash属性" class="headerlink" title="使用flash属性"></a>使用flash属性</h4><p>略。。参考Spring in Action 第四版 7.5</p>]]></content>
    
    <summary type="html">
    
      &lt;h3 id=&quot;1-Spring中的异常&quot;&gt;&lt;a href=&quot;#1-Spring中的异常&quot; class=&quot;headerlink&quot; title=&quot;1.Spring中的异常&quot;&gt;&lt;/a&gt;1.Spring中的异常&lt;/h3&gt;&lt;p&gt;首先，要对异常有一个清晰的认识，刚学JAVA那会儿对异常的了解很是朦胧，只知道try catch 可以捕获异常，可以输出异常。打印出异常信息，对何时应该捕获，何时抛异常并不在乎。知道上班后才渐渐的对异常处理重视起来。&lt;br&gt;一个程序不可能不出异常情况，关键在于我们能尽可能的预见将要发生的异常并提前对可能发生的异常进行处理，提高程序的健壮性。（PS:今天看了猩球崛起3，有点失望）&lt;br&gt;&lt;img src=&quot;/images/Spring_in_Action/xqjq.jpg&quot; alt=&quot;小小牛博客&quot;&gt;&lt;br&gt;Spring提供了多种方式将异常转换为响应：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;特定的Spring异常将会自动映射为指定的HTTP状态码；&lt;/li&gt;
&lt;li&gt;异常上可以添加@ResponseStatus注解，从而将其映射为某一个HTTP状态码；&lt;/li&gt;
&lt;li&gt;在方法上可以添加@ExceptionHandler注解，使其用来处理异常&lt;/li&gt;
&lt;/ul&gt;
    
    </summary>
    
      <category term="看书笔记" scheme="http://blog.lyq3.com/categories/%E7%9C%8B%E4%B9%A6%E7%AC%94%E8%AE%B0/"/>
    
      <category term="Spring in Action" scheme="http://blog.lyq3.com/categories/%E7%9C%8B%E4%B9%A6%E7%AC%94%E8%AE%B0/Spring-in-Action/"/>
    
    
      <category term="Exception" scheme="http://blog.lyq3.com/tags/Exception/"/>
    
      <category term="Spring" scheme="http://blog.lyq3.com/tags/Spring/"/>
    
      <category term="Spring in Action" scheme="http://blog.lyq3.com/tags/Spring-in-Action/"/>
    
      <category term="SpringMVC" scheme="http://blog.lyq3.com/tags/SpringMVC/"/>
    
  </entry>
  
  <entry>
    <title>SpringMvc-文件上传</title>
    <link href="http://blog.lyq3.com/2017/09/13/Spring_in_Action/SpringMVC3/"/>
    <id>http://blog.lyq3.com/2017/09/13/Spring_in_Action/SpringMVC3/</id>
    <published>2017-09-13T14:30:40.000Z</published>
    <updated>2018-04-30T10:44:26.098Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>文件上传我一直感觉是个麻烦事，一提到文件上传我脑海中浮现的是二进制、byte数组、输入输出流、读写文件<br>感觉头都大了，所以为了以后不再头大，正好最近在看Spring in Action 正好研究下，用各种姿势上传文件。传各种小黄片^-^。</p></blockquote><p><img src="/images/Spring_in_Action/timg.jpg" alt="SpringMVC--小小牛博客"></p><h3 id="1-Spring-MVC中配置文件上传"><a href="#1-Spring-MVC中配置文件上传" class="headerlink" title="1.Spring MVC中配置文件上传"></a>1.Spring MVC中配置文件上传</h3><p>下面是我随手上传一张图片的请求体：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">------WebKitFormBoundaryiYXA5UiOJRgWnKnt</span><br><span class="line">Content-Disposition: form-data; name=<span class="string">"upfile"</span>; filename=<span class="string">"0.jpg"</span></span><br><span class="line">Content-Type: image/jpeg</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">------WebKitFormBoundaryiYXA5UiOJRgWnKnt--</span><br></pre></td></tr></table></figure><a id="more"></a><p>DispatcherServlet并没有实现任何解析multipart请求数据的功能。它将该任务委托给了Spring中MultipartResolver策略接口的实现，通过这个实现类来解析multipart请求中的内容。从Spring 3.1开始，Spring内置了两个MultipartResolver的实现供我们选择：</p><ul><li>CommonsMultipartResolver：使用Jakarta CommonsFileUpload解析multipart请求；</li><li>StandardServletMultipartResolver：依赖于Servlet 3.0对multipart请求的支持（始于Spring 3.1）。</li></ul><blockquote><p>一般优选StandardServletMultipartResolver,不过为什么我更喜欢StandardServletMultipartResolver呢？</p></blockquote><h4 id="使用Servlet-3-0解析multipart请求（StandardServletMultipartResolver）"><a href="#使用Servlet-3-0解析multipart请求（StandardServletMultipartResolver）" class="headerlink" title="使用Servlet 3.0解析multipart请求（StandardServletMultipartResolver）"></a>使用Servlet 3.0解析multipart请求（StandardServletMultipartResolver）</h4><p>在Spring应用上下文中，将其声明为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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> MultipartResolver <span class="title">multipartResolver</span><span class="params">()</span> <span class="keyword">throws</span> IOException</span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> StandardServletMultipartResolver();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上面配置还不能正常使用，我们还需要设置存放路径，限制文件大小、类型等<br>我们需在web.xml或Servlet初始化类中配置。<br>如果我们采用Servlet初始化类的方式来配置DispatcherServlet的话，这个初始化类应该已经实现了WebApplicationInitializer，那我们可以在Servlet registration上调用setMultipartConfig()方法，传入一<br>个MultipartConfigElement实例，</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">customizeRegistration</span><span class="params">(Dynamic registration)</span></span>&#123;</span><br><span class="line">  registration.setMultipartConfig (</span><br><span class="line">  <span class="keyword">new</span> MultipartConfigElement(</span><br><span class="line">            <span class="string">"/tem/uploads"</span>,<span class="comment">//路径</span></span><br><span class="line">            <span class="number">2097152</span>,  <span class="comment">//上传文件的最大容量（以字节为单位）。默认是没有限制的。</span></span><br><span class="line">            <span class="number">4194304</span>,  <span class="comment">//整个multipart请求的最大容量（以字节为单位），默认是没有限制的</span></span><br><span class="line">            <span class="number">0</span> <span class="comment">//在上传的过程中，如果文件大小达到了一个指定最大容量（以字</span></span><br><span class="line">              <span class="comment">//节为单位），将会写入到临时文件路径中。默认值为0，也就是</span></span><br><span class="line">              <span class="comment">//所有上传的文件都会写入到磁盘上</span></span><br><span class="line">  )</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>配置类参考<a href="/2017/09/04/Spring_in_Action/SpringMVC-1/">SpringMVC起步-构建Web应用程序</a></p></blockquote><p>如果使用传统的xml配置，则在web.xml中配置如下：<br><img src="/images/Spring_in_Action/mvc6.png" alt="SpringMVC--小小牛博客"></p><h4 id="使用Jakarta-Commons-FileUpload-multipart解析器"><a href="#使用Jakarta-Commons-FileUpload-multipart解析器" class="headerlink" title="使用Jakarta Commons FileUpload multipart解析器"></a>使用Jakarta Commons FileUpload multipart解析器</h4><blockquote><p>Spring内置了CommonsMultipartResolver，可以作为StandardServletMultipartResolver的替代方案</p></blockquote><p>将CommonsMultipartResolver声明为Spring bean的最简单方式如下：<br><img src="/images/Spring_in_Action/mvc7.png" alt="SpringMVC--小小牛博客"></p><h3 id="2-处理multipart-请求（接收上传文件）"><a href="#2-处理multipart-请求（接收上传文件）" class="headerlink" title="2.处理multipart 请求（接收上传文件）"></a>2.处理multipart 请求（接收上传文件）</h3><ul><li><form>表单上设置enctype=multipart/form-data,也就是说Content-Type:multipart/form-data;</form></li><li>控制器接收参数上添加@RequestPart注解<br><img src="/images/Spring_in_Action/mvc8.png" alt="SpringMVC--小小牛博客"></li></ul><p>这种用数组接收的方式很原始，没用文件类型，文件名等等，还要自己写流存文件。</p><h4 id="接收MultipartFile"><a href="#接收MultipartFile" class="headerlink" title="接收MultipartFile"></a>接收MultipartFile</h4><p>将数组换成MultipartFile接收，内置方法如下：<br><img src="/images/Spring_in_Action/file1.png" alt="SpringMVC--小小牛博客"></p><h4 id="将MultipartFile保存到七牛云，阿里云等云存储中"><a href="#将MultipartFile保存到七牛云，阿里云等云存储中" class="headerlink" title="将MultipartFile保存到七牛云，阿里云等云存储中"></a>将MultipartFile保存到七牛云，阿里云等云存储中</h4><blockquote><p>也就是利用MultipartFile获取文件信息保存到远程服务器，暂不写</p></blockquote><h4 id="Part接口：Spring-MultipartFile的替代方案"><a href="#Part接口：Spring-MultipartFile的替代方案" class="headerlink" title="Part接口：Spring MultipartFile的替代方案"></a>Part接口：Spring MultipartFile的替代方案</h4><p>和上面的API差不多，值得一提的是，如果在编写控制器方法的时候，通过Part参数的形式<br>接受文件上传，那么就没有必要配置MultipartResolver了。只有使用MultipartFile的时候，我们才需要<br>MultipartResolver。<br><img src="/images/Spring_in_Action/file2.png" alt="SpringMVC--小小牛博客"></p><h3 id="3-上传案列"><a href="#3-上传案列" class="headerlink" title="3.上传案列"></a>3.上传案列</h3><blockquote><p>下面是我在项目中随手写的一个上传文件类，本来项目使用的是百度的Ueditor上传文件，但是Ueditor是上传文件到项目的目录下面，每次部署war包都会覆盖原来的上传文件，必须先备份上传的文件，部署完项目后又重新传上去，很是麻烦。由于是前后端分离，前端静态资源部署在Nginx所以自己写了个上传请求将文件传到Nginx目录下边。</p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Controller</span></span><br><span class="line"><span class="meta">@Api</span>(tags=<span class="string">"文件上传"</span>)</span><br><span class="line"><span class="meta">@RequestMapping</span>(<span class="string">"/file"</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">FileUploadController</span> </span>&#123;</span><br><span class="line">    <span class="comment">/**文件存放主路径*/</span></span><br><span class="line">    <span class="meta">@Value</span>(<span class="string">"$&#123;cnct.upload.file.path&#125;"</span>)</span><br><span class="line">    <span class="keyword">private</span> String baseFilePath;</span><br><span class="line">    <span class="comment">/**系统路径分隔符*/</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> String FILE_SEPARATOR = System.getProperty(<span class="string">"file.separator"</span>);</span><br><span class="line">    <span class="comment">/**文件访问前缀，一般为Nginx路径*/</span></span><br><span class="line">    <span class="meta">@Value</span>(<span class="string">"$&#123;cnct.request.file.path&#125;"</span>)</span><br><span class="line">    <span class="keyword">private</span> String requestFilePath;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 多文件上传</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> request</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@RequestMapping</span>(<span class="string">"/multipleFileUpload"</span>)</span><br><span class="line">    <span class="meta">@ResponseBody</span></span><br><span class="line">    <span class="keyword">public</span> List&lt;Map&lt;String, Object&gt;&gt; multipleFileUpload(HttpServletRequest request)&#123;</span><br><span class="line">        List&lt;Map&lt;String,Object&gt;&gt; res = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line">        List&lt;MultipartFile&gt; files = ((MultipartHttpServletRequest) request).getFiles(<span class="string">"file"</span>);</span><br><span class="line">        <span class="keyword">if</span>(files == <span class="keyword">null</span> || files.size() == <span class="number">0</span>)&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">for</span>(MultipartFile file : files)&#123;<span class="comment">//遍历存文件</span></span><br><span class="line">            Map&lt;String,Object&gt; map = <span class="keyword">new</span> HashMap&lt;&gt;();</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 截取上传文件的文件名</span></span><br><span class="line">            String uploadFilePath = file.getOriginalFilename();</span><br><span class="line">            String fileName = uploadFilePath</span><br><span class="line">                    .substring(uploadFilePath.lastIndexOf(<span class="string">'\\'</span>) + <span class="number">1</span>,</span><br><span class="line">                            uploadFilePath.indexOf(<span class="string">'.'</span>));</span><br><span class="line">            <span class="comment">// 截取上传文件的后缀</span></span><br><span class="line">            String suffixName = uploadFilePath.substring(</span><br><span class="line">                    uploadFilePath.indexOf(<span class="string">'.'</span>), uploadFilePath.length());</span><br><span class="line">            String filePath = baseFilePath + <span class="keyword">new</span> DateTime().getYear()+FILE_SEPARATOR +<span class="keyword">new</span> DateTime().getMonthOfYear()+FILE_SEPARATOR +<span class="keyword">new</span> DateTime().getDayOfMonth();</span><br><span class="line">            String realFileName = String.valueOf(<span class="keyword">new</span> DateTime().getMillis()) + suffixName;</span><br><span class="line">            File f = <span class="keyword">new</span> File(filePath+FILE_SEPARATOR+realFileName);</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span>(!f.getParentFile().exists())&#123;</span><br><span class="line">                f.getParentFile().mkdirs();</span><br><span class="line">            &#125;</span><br><span class="line">                f.createNewFile();</span><br><span class="line">                file.transferTo(f);</span><br><span class="line">                map.put(<span class="string">"original"</span>,fileName+suffixName);</span><br><span class="line">                map.put(<span class="string">"size"</span>,file.getSize());</span><br><span class="line">                map.put(<span class="string">"state"</span>,<span class="string">"SUCCESS"</span>);</span><br><span class="line">                map.put(<span class="string">"title"</span>,realFileName);</span><br><span class="line">                map.put(<span class="string">"type"</span>,suffixName);</span><br><span class="line">                map.put(<span class="string">"url"</span>,requestFilePath+<span class="keyword">new</span> DateTime().getYear()+</span><br><span class="line">                        <span class="string">"/"</span> +<span class="keyword">new</span> DateTime().getMonthOfYear()+</span><br><span class="line">                        <span class="string">"/"</span> +<span class="keyword">new</span> DateTime().getDayOfMonth()+</span><br><span class="line">                        <span class="string">"/"</span> +realFileName);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">                System.err.println(<span class="string">"文件不存在或者文件无权限"</span>);</span><br><span class="line">                map.put(<span class="string">"state"</span>,<span class="string">"ERROR"</span>);</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">            res.add(map);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 单文件上传</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> file</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@RequestMapping</span>(<span class="string">"/singleFileUpload"</span>)</span><br><span class="line">    <span class="meta">@ResponseBody</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Map&lt;String , Object&gt; <span class="title">singleFileUpload</span><span class="params">(MultipartFile file)</span></span>&#123;</span><br><span class="line">        Map&lt;String,Object&gt; res = <span class="keyword">new</span> HashMap&lt;&gt;();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (file.isEmpty()) &#123;</span><br><span class="line">            res.put(<span class="string">"state"</span>,<span class="string">"ERROR"</span>);</span><br><span class="line">            <span class="keyword">return</span> res;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 截取上传文件的文件名</span></span><br><span class="line">        String uploadFilePath = file.getOriginalFilename();</span><br><span class="line">        String fileName = uploadFilePath</span><br><span class="line">                .substring(uploadFilePath.lastIndexOf(<span class="string">'\\'</span>) + <span class="number">1</span>,</span><br><span class="line">                        uploadFilePath.indexOf(<span class="string">'.'</span>));</span><br><span class="line">        <span class="comment">// 截取上传文件的后缀</span></span><br><span class="line">        String suffixName = uploadFilePath.substring(</span><br><span class="line">                uploadFilePath.indexOf(<span class="string">'.'</span>), uploadFilePath.length());</span><br><span class="line">        String filePath = baseFilePath + <span class="keyword">new</span> DateTime().getYear()+FILE_SEPARATOR +<span class="keyword">new</span> DateTime().getMonthOfYear()+FILE_SEPARATOR +<span class="keyword">new</span> DateTime().getDayOfMonth();</span><br><span class="line">        String realFileName = String.valueOf(<span class="keyword">new</span> DateTime().getMillis()) + suffixName;</span><br><span class="line">        File f = <span class="keyword">new</span> File(filePath+FILE_SEPARATOR+realFileName);</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span>(!f.getParentFile().exists())&#123;</span><br><span class="line">                f.getParentFile().mkdirs();</span><br><span class="line">            &#125;</span><br><span class="line">            f.createNewFile();</span><br><span class="line">            file.transferTo(f);</span><br><span class="line">            res.put(<span class="string">"original"</span>,fileName+suffixName);</span><br><span class="line">            res.put(<span class="string">"size"</span>,file.getSize());</span><br><span class="line">            res.put(<span class="string">"state"</span>,<span class="string">"SUCCESS"</span>);</span><br><span class="line">            res.put(<span class="string">"title"</span>,realFileName);</span><br><span class="line">            res.put(<span class="string">"type"</span>,suffixName);</span><br><span class="line">            res.put(<span class="string">"url"</span>,requestFilePath+<span class="keyword">new</span> DateTime().getYear()+</span><br><span class="line">                    <span class="string">"/"</span> +<span class="keyword">new</span> DateTime().getMonthOfYear()+</span><br><span class="line">                    <span class="string">"/"</span> +<span class="keyword">new</span> DateTime().getDayOfMonth()+</span><br><span class="line">                    <span class="string">"/"</span> +realFileName);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">            System.err.println(<span class="string">"文件不存在或者文件无权限"</span>);</span><br><span class="line">            res.put(<span class="string">"state"</span>,<span class="string">"ERROR"</span>);</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>项目并发量不大，所以代码没过多优化设计。项目使用的是SpringBoot<br>spring:<br>  http:<br>    multipart:<br>               max-file-size: 100MB<br>               max-request-size: 100MB<br>               enabled: true<br>SpringBoot大发好，multipart都不需要配那么多那么麻烦，真是浪费生命</p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;文件上传我一直感觉是个麻烦事，一提到文件上传我脑海中浮现的是二进制、byte数组、输入输出流、读写文件&lt;br&gt;感觉头都大了，所以为了以后不再头大，正好最近在看Spring in Action 正好研究下，用各种姿势上传文件。传各种小黄片^-^。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;/images/Spring_in_Action/timg.jpg&quot; alt=&quot;SpringMVC--小小牛博客&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;1-Spring-MVC中配置文件上传&quot;&gt;&lt;a href=&quot;#1-Spring-MVC中配置文件上传&quot; class=&quot;headerlink&quot; title=&quot;1.Spring MVC中配置文件上传&quot;&gt;&lt;/a&gt;1.Spring MVC中配置文件上传&lt;/h3&gt;&lt;p&gt;下面是我随手上传一张图片的请求体：&lt;/p&gt;
&lt;figure class=&quot;highlight java&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;------WebKitFormBoundaryiYXA5UiOJRgWnKnt&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;Content-Disposition: form-data; name=&lt;span class=&quot;string&quot;&gt;&quot;upfile&quot;&lt;/span&gt;; filename=&lt;span class=&quot;string&quot;&gt;&quot;0.jpg&quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;Content-Type: image/jpeg&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;------WebKitFormBoundaryiYXA5UiOJRgWnKnt--&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
    
    </summary>
    
      <category term="看书笔记" scheme="http://blog.lyq3.com/categories/%E7%9C%8B%E4%B9%A6%E7%AC%94%E8%AE%B0/"/>
    
      <category term="Spring in Action" scheme="http://blog.lyq3.com/categories/%E7%9C%8B%E4%B9%A6%E7%AC%94%E8%AE%B0/Spring-in-Action/"/>
    
    
      <category term="Spring" scheme="http://blog.lyq3.com/tags/Spring/"/>
    
      <category term="Spring in Action" scheme="http://blog.lyq3.com/tags/Spring-in-Action/"/>
    
      <category term="SpringMVC" scheme="http://blog.lyq3.com/tags/SpringMVC/"/>
    
      <category term="文件上传" scheme="http://blog.lyq3.com/tags/%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0/"/>
    
  </entry>
  
  <entry>
    <title>SpringMvc-渲染Web视图</title>
    <link href="http://blog.lyq3.com/2017/09/10/Spring_in_Action/SpringMvc-2/"/>
    <id>http://blog.lyq3.com/2017/09/10/Spring_in_Action/SpringMvc-2/</id>
    <published>2017-09-10T14:34:07.000Z</published>
    <updated>2018-04-30T10:44:26.099Z</updated>
    
    <content type="html"><![CDATA[<h3 id="1-理解SpringMVC视图解析"><a href="#1-理解SpringMVC视图解析" class="headerlink" title="1.理解SpringMVC视图解析"></a>1.理解SpringMVC视图解析</h3><blockquote><p>上一篇讲到SpringMVC执行流程，DispatcherServlet会将模型和视图名交给视图解析器来解析生成响应视图</p></blockquote><p><img src="/images/Spring_in_Action/mvc2.jpg" alt="SpringMVC--小小牛博客"></p><p>Spring MVC定义了一个名为ViewResolver的接口，它大致如下所示：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">ViewResolver</span></span>&#123;</span><br><span class="line">  <span class="function">View <span class="title">resolveViewName</span><span class="params">(String viewName, Locale locale)</span> <span class="keyword">throws</span> Exception</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><a id="more"></a><p>当给resolveViewName()方法传入一个视图名和Locale对象时，<br>它会返回一个View实例。View是另外一个接口，如下所示：</p><blockquote><p>Locale对象，以便于恰当地格式化地域<br>相关的值，如日期和货币。信息标签可以借助Spring的信息资源和<br>Locale，从而选择适当的信息渲染到HTML之中。通例如：JSTL能够获得Locale对象以及Spring中配置的信息资源</p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">View</span></span>&#123;</span><br><span class="line">  <span class="function">String <span class="title">getContentType</span><span class="params">()</span></span>;</span><br><span class="line">  <span class="function"><span class="keyword">void</span> <span class="title">render</span><span class="params">(Map&lt;String,?&gt; model, HttpServletRequest request,HttpServletResponse response)</span> <span class="keyword">throws</span> Exception</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>View接口的任务就是接受模型以及Servlet的request和response对象，<br>并将输出结果渲染到response中。</p><div class="note success"><p>我们只需实现这两个接口就可以解析视图了，不过Spring自带了13个视图解析器，能够将逻辑视图名转换为物理实现。</p></div><table><thead><tr><th>视图解析器</th><th style="text-align:left">描述</th></tr></thead><tbody><tr><td>BeanNameViewResolver</td><td style="text-align:left">将视图解析为Spring应用上下文中的bean，其中bean的ID与视图的名字相同</td></tr><tr><td>ContentNegotiatingViewResolver</td><td style="text-align:left">通过考虑客户端需要的内容类型来解析视图，委托给另外一个能够产生对应内容类型的视图解析器</td></tr><tr><td>FreeMarkerViewResolver</td><td style="text-align:left">将视图解析为FreeMarker模板</td></tr><tr><td>InternalResourceViewResolver</td><td style="text-align:left">将视图解析为Web应用的内部资源（一般为JSP）</td></tr><tr><td>JasperReportsViewResolver</td><td style="text-align:left">将视图解析为JasperReports定义</td></tr><tr><td>ResourceBundleViewResolver</td><td style="text-align:left">将视图解析为资源bundle（一般为属性文件）</td></tr><tr><td>TilesViewResolver</td><td style="text-align:left">将视图解析为Apache Tile定义，其中tile ID与视图名称相同。注意有两个不同的TilesViewResolver实现，分别对应于Tiles 2.0和Tiles 3.0</td></tr><tr><td>UrlBasedViewResolver</td><td style="text-align:left">直接根据视图的名称解析视图，视图的名称会匹配一个物理视图的定义</td></tr><tr><td>VelocityLayoutViewResolver</td><td style="text-align:left">将视图解析为Velocity布局，从不同的Velocity模板中组合页面</td></tr><tr><td>VelocityViewResolver</td><td style="text-align:left">将视图解析为Velocity模板</td></tr><tr><td>XmlViewResolver</td><td style="text-align:left">将视图解析为特定XML文件中的bean定义。类似于BeanName-ViewResolver</td></tr><tr><td>XsltViewResolver</td><td style="text-align:left">将视图解析为XSLT转换后的结果</td></tr></tbody></table><p>我们常用的也就那么几个，InternalResourceViewResolver一般会<br>用于JSP，TilesViewResolver用于Apache Tiles视图（布局），而FreeMarkerViewResolver和VelocityViewResolver分别<br>对应FreeMarker和Velocity模板视图</p><h3 id="2-配置JSP视图解析器"><a href="#2-配置JSP视图解析器" class="headerlink" title="2.配置JSP视图解析器"></a>2.配置JSP视图解析器</h3><p>InternalResourceViewResolver会将视图名解析为JSP文件。另外，如果在你的JSP页面中使用了JSP标准标签库（JavaServer Pages Standard Tag Library，JSTL）的话，InternalResourceViewResolver能够将视图名解析为JstlView形式的JSP文件，从而将JSTL本地化和资源bundle变量暴露给JSTL的格式化（formatting）和信息（message）标签</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> ViewResolver <span class="title">viewResolver</span><span class="params">()</span> </span>&#123;</span><br><span class="line">  InternalResourceViewResolver resolver = <span class="keyword">new</span> InternalResourceViewResolver();</span><br><span class="line">  resolver.setPrefix(<span class="string">"/WEB-INF/views/"</span>);</span><br><span class="line">  resolver.setSuffix(<span class="string">".jsp"</span>);</span><br><span class="line">  <span class="keyword">return</span> resolver;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>当然如果喜欢xml配置方式也可以用xml配置，我个人喜欢JavaConf方式</p></blockquote><p><strong> 支持JSTL </strong></p><p>如果想让InternalResourceViewResolver将视图解析为JstlView，而不是InternalResourceView的话，<br>那么我们只需设置它的viewClass属性即可：<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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> ViewResolver <span class="title">viewResolver</span><span class="params">()</span> </span>&#123;</span><br><span class="line">  InternalResourceViewResolver resolver = <span class="keyword">new</span> InternalResourceViewResolver();</span><br><span class="line">  resolver.setPrefix(<span class="string">"/WEB-INF/views/"</span>);</span><br><span class="line">  resolver.setSuffix(<span class="string">".jsp"</span>);</span><br><span class="line">  resolver.setViewClass(org.springframework.web.servlet.view.JstlView.class)</span><br><span class="line">  <span class="keyword">return</span> resolver;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h3 id="3-表单校验"><a href="#3-表单校验" class="headerlink" title="3.表单校验"></a>3.表单校验</h3><p>大多数项目都会有表单的提交，比如注册、登录等，我们通常会在前台通过JS对用户的输入进行校验，也可后台进行数据校验，<br>前台校验的优点是不用发送请求，速度快，缺点是不安全，前端校验可以被轻易的绕开。后端的校验恰好相反。<br>平时我们大多会在两端都进行校验，前端初步校验提高用户友好性，后端校验提高安全性。<br>下面我们谈谈后端的校验。</p><p>最简单粗暴的方法是在后端获取参数后逐个写逻辑判断。这种方式会让我们的Controller很乱很臃肿。与其让校验逻辑弄乱我们的处理器方法，还不如使用Spring对Java校验API（Java Validation API，又称JSR-303）的支持。从Spring 3.0开始，在Spring MVC中提供了对Java校验API的支持。在Spring MVC中要使用Java校验API的话，并不需要什么额外的配置。只要保证在类路径下包含这个Java API的实现即可，比如Hibernate Validator。Java校验API定义了多个注解，这些注解可以放到属性上，从而限制这些属性的值。所有的注解都位于javax.validation.constraints包中</p><p>以下是JAVA校验API提供的一些注解：</p><table><thead><tr><th>注解</th><th style="text-align:left">描述</th></tr></thead><tbody><tr><td>@AssertFalse</td><td style="text-align:left">所注解的元素必须是Boolean类型，并且值为false</td></tr><tr><td>@AssertTrue</td><td style="text-align:left">所注解的元素必须是Boolean类型，并且值为true</td></tr><tr><td>@DecimalMax</td><td style="text-align:left">所注解的元素必须是数字，并且它的值要小于或等于给定的BigDecimalString值</td></tr><tr><td>@DecimalMin</td><td style="text-align:left">所注解的元素必须是数字，并且它的值要大于或等于给定的BigDecimalString值</td></tr><tr><td>@Digits</td><td style="text-align:left">所注解的元素必须是数字，并且它的值必须有指定的位数</td></tr><tr><td>@Future</td><td style="text-align:left">所注解的元素的值必须是一个将来的日期</td></tr><tr><td>@Max</td><td style="text-align:left">所注解的元素必须是数字，并且它的值要小于或等于给定的值</td></tr><tr><td>@Min</td><td style="text-align:left">所注解的元素必须是数字，并且它的值要大于或等于给定的值</td></tr><tr><td>@NotNull</td><td style="text-align:left">所注解元素的值必须不能为null</td></tr><tr><td>@Null</td><td style="text-align:left">所注解元素的值必须为null</td></tr><tr><td>@Past</td><td style="text-align:left">所注解的元素的值必须是一个已过去的日期</td></tr><tr><td>@Pattern</td><td style="text-align:left">所注解的元素的值必须匹配给定的正则表达式</td></tr><tr><td>@Size</td><td style="text-align:left">所注解的元素的值必须是String、集合或数组，并且它的长度要符合给定的范围</td></tr></tbody></table><p>例如：</p><blockquote><p>实体类：</p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> spittr;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> javax.validation.constraints.NotNull;</span><br><span class="line"><span class="keyword">import</span> javax.validation.constraints.Size;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.apache.commons.lang3.builder.EqualsBuilder;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.lang3.builder.HashCodeBuilder;</span><br><span class="line"><span class="keyword">import</span> org.hibernate.validator.constraints.Email;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Spitter</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">private</span> Long id;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@NotNull</span></span><br><span class="line">  <span class="meta">@Size</span>(min=<span class="number">5</span>, max=<span class="number">16</span>)</span><br><span class="line">  <span class="keyword">private</span> String username;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@NotNull</span></span><br><span class="line">  <span class="meta">@Size</span>(min=<span class="number">5</span>, max=<span class="number">25</span>)</span><br><span class="line">  <span class="keyword">private</span> String password;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@NotNull</span></span><br><span class="line">  <span class="meta">@Size</span>(min=<span class="number">2</span>, max=<span class="number">30</span>)</span><br><span class="line">  <span class="keyword">private</span> String firstName;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@NotNull</span></span><br><span class="line">  <span class="meta">@Size</span>(min=<span class="number">2</span>, max=<span class="number">30</span>)</span><br><span class="line">  <span class="keyword">private</span> String lastName;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@NotNull</span></span><br><span class="line">  <span class="meta">@Email</span></span><br><span class="line">  <span class="keyword">private</span> String email;</span><br><span class="line">  <span class="comment">//get  set 省略</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>Controller 方法</p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@RequestMapping</span>(value=<span class="string">"/register"</span>, method=POST)</span><br><span class="line">  <span class="function"><span class="keyword">public</span> String <span class="title">processRegistration</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">      @Valid Spitter spitter, //校验输入</span></span></span><br><span class="line"><span class="function"><span class="params">      Errors errors)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (errors.hasErrors()) &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="string">"registerForm"</span>; <span class="comment">//校验出现错误则返回表单</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    spitterRepository.save(spitter);</span><br><span class="line">    <span class="keyword">return</span> <span class="string">"redirect:/spitter/"</span> + spitter.getUsername();</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><p>如果有校验出现错误的话，那么这些错误可以通过Errors对象进行访问，现在这个对象已作为processRegistration()方法的参数。（很重要一点需要注意，Errors参数要紧跟在带有@Valid注解的参数后面，@Valid注解所标注的就是要检验的参数。）processRegistration()方法所做的第一件事就是调用Errors.hasErrors()来检查是否有错误。</p><blockquote><p>但这时候返回表单页面会丢失之前填的数据</p></blockquote><h3 id="4-使用Spring的JSP库"><a href="#4-使用Spring的JSP库" class="headerlink" title="4.使用Spring的JSP库"></a>4.使用Spring的JSP库</h3><blockquote><p>使用这个标签库可以将错误信息友好的显示在页面，也会保留验证钱的数据（解决上面的问题），当然也可以自己写处理，方式很多，不局限这一种</p></blockquote><p>当为JSP添加功能时，标签库是一种很强大的方式，能够避免在脚本块中直接编写Java代码。Spring提供了两个JSP标签库，用来帮助定义Spring MVC Web的视图。其中一个标签库会用来渲染HTML表单标签，这些标签可以绑定model中的某个属性。另外一个标签库包含了一些工具类标签，我们随时都可以非常便利地使用它们。</p><div class="note success"><p>具体使用方法请参考Spring in Action 6.2.2</p></div><h3 id="5-使用Apache-Tiles视图定义布局"><a href="#5-使用Apache-Tiles视图定义布局" class="headerlink" title="5.使用Apache Tiles视图定义布局"></a>5.使用Apache Tiles视图定义布局</h3><p>到现在为止，我们很少关心应用中Web页面的布局问题。每个JSP完全负责定义自身的布局，在这方面其实这些JSP也没有做太多工作。假设我们想为应用中的所有页面定义一个通用的头部和底部。最原始的方式就是查找每个JSP模板，并为其添加头部和底部的HTML。但是这种方法的扩展性并不好，也难以维护。为每个页面添加这些元素会有一些初始成本，而后续的每次变更都会耗费类似的成本。更好的方式是使用布局引擎，如Apache Tiles，定义适用于所有页面的通用页面布局。Spring MVC以视图解析器的形式为Apache Tiles提供了支持，这个视图解析器能够将逻辑视图名解析为Tile定义。</p><p>来感受下：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">&lt;?xml version="1.0" encoding="ISO-8859-1" ?&gt;</span><br><span class="line"><span class="meta">&lt;!DOCTYPE tiles-definitions PUBLIC</span></span><br><span class="line"><span class="meta">       "-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN"</span></span><br><span class="line"><span class="meta">       "http://tiles.apache.org/dtds/tiles-config_3_0.dtd"&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">tiles-definitions</span>&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="tag">&lt;<span class="name">definition</span> <span class="attr">name</span>=<span class="string">"base"</span> <span class="attr">template</span>=<span class="string">"/WEB-INF/layout/page.jsp"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">put-attribute</span> <span class="attr">name</span>=<span class="string">"header"</span> <span class="attr">value</span>=<span class="string">"/WEB-INF/layout/header.jsp"</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">put-attribute</span> <span class="attr">name</span>=<span class="string">"footer"</span> <span class="attr">value</span>=<span class="string">"/WEB-INF/layout/footer.jsp"</span> /&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">definition</span>&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="tag">&lt;<span class="name">definition</span> <span class="attr">name</span>=<span class="string">"home"</span> <span class="attr">extends</span>=<span class="string">"base"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">put-attribute</span> <span class="attr">name</span>=<span class="string">"body"</span> <span class="attr">value</span>=<span class="string">"/WEB-INF/views/home.jsp"</span> /&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">definition</span>&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="tag">&lt;<span class="name">definition</span> <span class="attr">name</span>=<span class="string">"registerForm"</span> <span class="attr">extends</span>=<span class="string">"base"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">put-attribute</span> <span class="attr">name</span>=<span class="string">"body"</span> <span class="attr">value</span>=<span class="string">"/WEB-INF/views/registerForm.jsp"</span> /&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">definition</span>&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="tag">&lt;<span class="name">definition</span> <span class="attr">name</span>=<span class="string">"profile"</span> <span class="attr">extends</span>=<span class="string">"base"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">put-attribute</span> <span class="attr">name</span>=<span class="string">"body"</span> <span class="attr">value</span>=<span class="string">"/WEB-INF/views/profile.jsp"</span> /&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">definition</span>&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="tag">&lt;<span class="name">definition</span> <span class="attr">name</span>=<span class="string">"spittles"</span> <span class="attr">extends</span>=<span class="string">"base"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">put-attribute</span> <span class="attr">name</span>=<span class="string">"body"</span> <span class="attr">value</span>=<span class="string">"/WEB-INF/views/spittles.jsp"</span> /&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">definition</span>&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="tag">&lt;<span class="name">definition</span> <span class="attr">name</span>=<span class="string">"spittle"</span> <span class="attr">extends</span>=<span class="string">"base"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">put-attribute</span> <span class="attr">name</span>=<span class="string">"body"</span> <span class="attr">value</span>=<span class="string">"/WEB-INF/views/spittle.jsp"</span> /&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">definition</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">tiles-definitions</span>&gt;</span></span><br></pre></td></tr></table></figure><div class="note success"><p>具体使用方法请参考Spring in Action 6.3</p></div>]]></content>
    
    <summary type="html">
    
      &lt;h3 id=&quot;1-理解SpringMVC视图解析&quot;&gt;&lt;a href=&quot;#1-理解SpringMVC视图解析&quot; class=&quot;headerlink&quot; title=&quot;1.理解SpringMVC视图解析&quot;&gt;&lt;/a&gt;1.理解SpringMVC视图解析&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;上一篇讲到SpringMVC执行流程，DispatcherServlet会将模型和视图名交给视图解析器来解析生成响应视图&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;/images/Spring_in_Action/mvc2.jpg&quot; alt=&quot;SpringMVC--小小牛博客&quot;&gt;&lt;/p&gt;
&lt;p&gt;Spring MVC定义了一个名为ViewResolver的接口，它大致如下所示：&lt;/p&gt;
&lt;figure class=&quot;highlight java&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;class&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;title&quot;&gt;ViewResolver&lt;/span&gt;&lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;  &lt;span class=&quot;function&quot;&gt;View &lt;span class=&quot;title&quot;&gt;resolveViewName&lt;/span&gt;&lt;span class=&quot;params&quot;&gt;(String viewName, Locale locale)&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;throws&lt;/span&gt; Exception&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
    
    </summary>
    
      <category term="看书笔记" scheme="http://blog.lyq3.com/categories/%E7%9C%8B%E4%B9%A6%E7%AC%94%E8%AE%B0/"/>
    
      <category term="Spring in Action" scheme="http://blog.lyq3.com/categories/%E7%9C%8B%E4%B9%A6%E7%AC%94%E8%AE%B0/Spring-in-Action/"/>
    
    
      <category term="Spring" scheme="http://blog.lyq3.com/tags/Spring/"/>
    
      <category term="Spring in Action" scheme="http://blog.lyq3.com/tags/Spring-in-Action/"/>
    
      <category term="SpringMVC" scheme="http://blog.lyq3.com/tags/SpringMVC/"/>
    
  </entry>
  
  <entry>
    <title>【转】对于Java程序猿学习当中各个阶段的建议</title>
    <link href="http://blog.lyq3.com/2017/09/09/zhuan/zuoxiaolong/"/>
    <id>http://blog.lyq3.com/2017/09/09/zhuan/zuoxiaolong/</id>
    <published>2017-09-09T14:34:07.000Z</published>
    <updated>2018-04-30T10:44:26.101Z</updated>
    
    <content type="html"><![CDATA[<h3 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h3><p>其实本来真的没打算写这篇文章，主要是LZ得记忆力不是很好，不像一些记忆力强的人，面试完以后，几乎能把自己和面试官的对话都给记下来。LZ自己当初面试完以后，除了记住一些聊过的知识点以外，具体的内容基本上忘得一干二净，所以写这篇文章其实是很有难度的。</p><p>但是，最近问LZ的人实在是太多了，为了避免重复回答，给自己省点力气，干脆就在这里统一回复了。</p><p>其实之前LZ写过一篇文章，但是那篇文章更多的是在讨论“面试前该不该刷题”这个话题，而这篇文章将会更加聚焦在面试前如何准备，以及工作当中如何学习这个话题上，而且会尽量写出一些干货。<br><a id="more"></a></p><p>第一个问题：阿里面试都问什么？</p><p>这个是让LZ最头疼的一个问题，也是群里的猿友们问的最多的一个问题。</p><p>说实话，LZ只能隐约想起并发、JVM、分布式、TCP/IP协议这些个关键字，具体的问题真的是几乎都没记住。而且就算LZ记住了，也告诉你了，你也背会了，但LZ觉得，在面试中，你被问到一模一样问题的可能性依然很小。</p><p>甚至，就算你运气好被问到了，你也照着背下来了，也不一定就能对你的面试起到正面的作用，因为面试官万一多问一句，你可能就露馅了，那还不如干脆点说不会更好。</p><p>LZ参加的是阿里的社招面试，而社招不同于校招，问题的范围其实是很随机的。因为能参加一些比较知名的互联网公司社招的人，70%以上都会有个3-5年的经验。这倒不是说一两年经验的同学没有机会进这些公司，而是因为这种公司，大部分情况下只招一些比较资深的开发和应届生，而不招那些处于中间阶段的人。而1-2年经验的同学，往往就刚好处于这个尴尬的阶段。</p><p>对于能有3-5年经验的这部分人中，每个人的经历又都不同，所擅长的点也不一样，因此这就会导致每个人的问题和范围都不太一样。</p><p>很少说有哪个知名的互联网公司，比如BAT、京东、360、搜狐、网易等这些公司，其社招面试还有固定的问题和模式，让你可以像应届生面试一样，在面试前靠临时抱佛脚度过这一关。</p><p>大部分公司在社招的时候，不光是阿里，其它公司也都一样（因为LZ在一年多前也参加过很多其它知名互联网公司的面试，详情见《记录2015年年初跳槽的经历！》），基本上都分为两个阶段的提问。</p><p>第一个阶段是主语言本身以及它的高级特性，第二个阶段是讲述自己的项目，并在中间穿插着问题。</p><p>所以，LZ不妨就这两个阶段，谈谈社招面试的准备，而不是去把阿里面试的过程背一遍。说实话，LZ也确实记不住，所以不要再问LZ阿里面试都会问哪些问题了，你看看上面那个连接里的文章，也会发现，LZ里面也基本上没有写具体的问题，原因是一样的，真的记不住啊。（就是因为记忆力的问题，导致LZ从小偏科，文科成绩一直堪忧，</p><p>社招面试如何准备</p><p>LZ会分为四个部分来谈论这个问题，由于LZ本身是Java出身，因此关于主语言的问题，都是与Java相关，其它语言的同学可以选择性忽略。此外，面试的时候一般面试官的问题都是环环相扣，逐渐深入的，这点在下面大家可以更明显的感受出来。</p><p>1、主语言本身以及它的高级特性。</p><p>主语言当然就是你平日里拿来赚钱的家伙。不要告诉LZ你没有主语言，你会N多种语言，或者是你精通N多种语言，你要非这么说的话，你可以来杭州试试，LZ保证不打死你，最多打残。</p><p>LZ的主语言很显然是Java，那么对于Java来说，它的语言本身以及它的高级特性，都有哪些比较容易在面试中问到呢？</p><p>一般情况下，主要有以下知识点很容易被问到。（PS：以下所列举的，都是一些Java相对而言比较高级一点的知识点，因为这里谈的是社招，而不是校招）</p><p>1）Java的数据结构相关的类实现原理，比如LinkedList，ArrayList，HashMap，TreeMap这一类的。以下简单模拟一个数据结构的连环炮。</p><p>比如，面试官先问你HashMap是不是有序的？</p><p>你肯定回答说，不是有序的。那面试官就会继续问你，有没有有顺序的Map实现类？</p><p>你如果这个时候说不知道的话，那这个问题就到此结束了。如果你说有TreeMap和LinkedHashMap。</p><p>那么面试官接下来就可能会问你，TreeMap和LinkedHashMap是如何保证它的顺序的？</p><p>如果你回答不上来，那么到此为止。如果你依然回答上来了，那么面试官还会继续问你，你觉得它们两个哪个的有序实现比较好？</p><p>如果你依然可以回答的话，那么面试官会继续问你，你觉得还有没有比它更好或者更高效的实现方式？</p><p>如果你还能说出来的话，那么就你所说的实现方式肯定依然可以问你很多问题。</p><p>以上就是一个面试官一步一步提问的例子。所以，如果你了解的不多，千万不要敷衍，因为可能下一个问题你就暴露了，还不如直接说不会，把这个问题结束掉，赶紧切换到你熟悉的领域。</p><p>2）Java并发包当中的类，它们都有哪些作用，以及它们的实现原理，这些类就是java.concurrent包下面的。与上面一样，咱们也简单的模拟一个并发包的连环炮。</p><p>比如面试官可能会先问你，如果想实现所有的线程一起等待某个事件的发生，当某个事件发生时，所有线程一起开始往下执行的话，有什么好的办法吗？</p><p>这个时候你可能会说可以用栅栏（Java的并发包中的CyclicBarrier），那么面试官就会继续问你，你知道它的实现原理吗？</p><p>如果你继续回答的话，面试官可能会继续问你，你还知道其它的实现方式吗？</p><p>如果你还能说出很多种实现方式的话，那么继续问你，你觉得这些方式里哪个方式更好？</p><p>如果你说出来某一个方式比较好的话，面试官依然可以继续问你，那如果让你来写的话，你觉得还有比它更好的实现方式吗？</p><p>如果你这个时候依然可以说出来你自己更好的实现方式，那么面试官肯定还会揪着这个继续问你。</p><p>为什么说面试的时候要引导面试官，原因就在这了。因为面试官的提问很多时候都是有迹可循的，你如果抓住了他的轨迹，能够猜到他下面很可能会问什么，那你在回答的时候就可以往你想要谈的方向去说。这样面试时就会显得更加从容，更加的游刃有余。</p><p>3）IO包和NIO包中的内容。这部分里面NIO会是重点，IO包大部分都会比较熟悉，因此可能会直接略过，直接问你NIO的内容。</p><p>IO包和NIO包的内容相对来说不是很多，首先NIO模型要熟悉，特别是其中的selector一定要非常清楚它的职责和实现原理。其实NIO的核心是IO线程池，一定要记住这个关键点。有的时候，面试官可能也会问你IO包的设计模式（装饰器模式），为什么要这样设计？</p><p>有的面试官还会问你有没有更好的设计，这个时候如果你不知道请果断说自己现在的水平有限，想不出来更好的设计，千万不要信口开河，随意YY。</p><p>4）Java的虚拟机的内容。这部分主要包括三部分，GC、类加载机制，以及内存。</p><p>一个GC部分简单的连环炮。</p><p>面试官可以先问你什么时候一个对象会被GC？</p><p>接着继续问你为什么要在这种时候对象才会被GC？</p><p>接着继续问你GC策略都有哪些分类？</p><p>你如果说出来了，继续问你这些策略分别都有什么优劣势？都适用于什么场景？</p><p>你继续说出来了以后，给你举个实际的场景，让你选择一个GC策略？</p><p>你如果选出来了，继续问你，为什么要选择这个策略？</p><p>下面是关于类加载机制的简单连环炮。</p><p>首先肯定是先问你Java的类加载器都有哪些？</p><p>回答了这些以后，可能会问你每个类加载器都加载哪些类？</p><p>说完以后，可能会问你这些类加载之间的父子关系是怎样的？</p><p>你在回答的时候可能会提到双亲委派模型，那么可以继续问你什么是双亲委派模型？</p><p>你解释完了以后，可能会继续问你，为什么Java的类加载器要使用双亲委派模型？</p><p>你回答完以后，可能会继续问你如何自定义自己的类加载器，自己的类加载器和Java自带的类加载器关系如何处理？</p><p>再来一个关于内存的连环炮。</p><p>首先肯定就是问你内存分为哪几部分，这些部分分别都存储哪些数据？</p><p>然后继续问你一个对象从创建到销毁都是怎么在这些部分里存活和转移的？</p><p>接着可能会问你，内存的哪些部分会参与GC的回收？</p><p>完事以后，可能还会问你Java的内存模型是怎么设计的？</p><p>你回答了以后，还会继续问你为什么要这么设计？</p><p>问完以后，还可能会让你结合内存模型的设计谈谈volatile关键字的作用？</p><p>你在谈的时候，肯定会提到可见性，那么接着可见性这三个字，还可以继续问你并发的内容。</p><p>基本上Java语言本身以及语言稍微高级点的内容就是以上部分，如果你能把以上四部分了解的非常透彻，那基本上Java这部分就没啥问题了，因为光以上的内容就够你跟面试官聊很久了。你聊这些聊得久了，自然问你其它问题的时间就会短点。</p><p>你从LZ写这些问题的过程也应该能感受出来，很多时候，面试官都是顺着一条线一路问下去的，如果你觉得这条线你不熟悉的话，就要及时拐弯，引导面试官去问其它方面的问题。千万不要一直往下深入，直到自己跳不出来为止，那就尴了个尬了。</p><p>2、讲述自己的项目，并在中间穿插着问题</p><p>这一部分是面试过程中必问，也是聊得最久的一个阶段。除非你前面的语言部分非常扎实，扎实到面试官问了一两个小时，依旧没有探出你对语言本身的了解到底有多深。否则的话，你一定逃不过自己的项目这一关，而且一般情况下聊得时间不会太短。</p><p>这一部分内容，一般的模式就是你自己去讲你做过的项目，然后面试官会冷不丁的让你去解释其中某一部分，比如让你解释当时为什么要这么做，或者问你现在觉得有没有更好的办法。而这些穿插的问题，大部分与你的项目所用到的技术有关。而你需要做的，就是充分、再充分的去总结自己做过的项目（尤其是最近的一两个项目），挖掘出一个甚至N个亮点，以备于到时候可以让面试官产生眼前一亮的感觉。如果你能达到这种效果的话，基本上离你成功就不远了。</p><p>这部分内容由于和每个人自己的经历息息相关，因此这里也没法列举可能问到的问题。这篇文章《程序员面经：面试前到底该不该刷题以及面试前该如何准备》是LZ之前写的，里面大概讨论了下如何在面试前总结，有兴趣的可以去了解一下。</p><p>3、额外的加分项</p><p>上面两个阶段基本上是必问的，还有一些加分项。这些加分项中，有些内容面试官也会问你（比如TCP/IP协议、算法），但更多的是会先问你了解不了解，你了解的话再继续聊，不了解的话就直接略过了，不至于因为这种问题而直接把你打入地狱。</p><p>下面LZ列举一下这些加分项，如果可以的话，这些加分项还是要争取一下的。</p><p>计算机系统原理。<br>网络通信协议（TCP/IP，HTTP等）。<br>数据结构与算法。<br>著名开源项目的源码。<br>你自己有很棒的开源项目。<br>你的个人博客。<br>待评论区补充。<br>这几项当中，对于前1-3项，如果你之前就比较了解，只是由于时间问题忘记了的话，还是可以临时抱佛脚一下的。至于后面4-6项，就需要你日常的积累了，不是一时半会儿能做到的。如果你平日里没有积累，那么后面这三个加分项只能抛弃了。</p><p>4、与你职位相关的内容</p><p>其实这最后一项是对前面三项的补充，你应该尽量去主攻和你面试的职位相关的内容。比如你面试一个实时计算的职位，那么你的算法最好要厉害，对于著名的实时计算开源项目要熟悉，最好阅读过源码，而且还要对分布式系统有一定的见解。</p><p>因此，这个第4部分没有具体的内容，只是提醒你，如果你很明确自己的面试职位，最好在面试前准备的时候，尽量朝职位的需求方向靠拢，这样成功的可能性更大。</p><p>对于Java程序猿学习的建议</p><p>这一部分其实也算是今天的重点，这一部分用来回答很多群里的朋友所问过的问题，那就是LZ你是如何学习Java的，能不能给点建议？</p><p>今天LZ是打算来点干货，因此咱们就不说一些学习方法和技巧了，直接来谈每个阶段要学习的内容甚至是一些书籍。这一部分的内容，同样适用于一些希望转行到Java的同学。</p><p>在大家看之前，LZ要先声明两点。</p><p>1、由于LZ本人是Java后端开发出身，因此所推荐的学习内容是Java Web和Java后端开发的路线，非Java Web和Java后端开发的同学请适当参考其学习思想即可，切勿照搬。</p><p>2、下面对于【第一部分】的推荐内容，目的是让你尽快成为一个可以参加工作的Java开发者，更适用于处于待业状态，准备转行Java的同学。如果你是在校学生，务必要在学好基础（比如计算机系统、算法、编译原理等等）的前提下，再考虑去进行下面的学习。</p><p>第一部分：对于尚未做过Java工作的同学，包括一些在校生以及刚准备转行Java的同学。</p><h3 id="一、Java基础"><a href="#一、Java基础" class="headerlink" title="一、Java基础"></a>一、Java基础</h3><p>首先去找一个Java的基础教程学一下，这里可以推荐一个地址，或者你也可以参照这个地址上去找相应的视频。</p><p>学习Java基础的时候，应该尽量多动手，很多时候，你想当然的事情，等你写出来运行一下，你就会发现不是这么回事儿，不信你就试试。</p><p>学完以上内容以后，你应该对Java有一个基本的了解了，你可以用Java语言写出一些简单的程序，并且你用的是最简单的编辑器，比如记事本。</p><p>这个时候，不要急于进入下一部分，留下几天好好写一些程序，尽可能熟悉这些基础内容。</p><h3 id="二、Web开发"><a href="#二、Web开发" class="headerlink" title="二、Web开发"></a>二、Web开发</h3><p>等你写上几天程序以后，你往往会比较迷茫，因为你写的东西似乎看起来毫无用处，比如实现一个简单的计算器，读取一个文件等。这个时候你就应该去学着写一些让你觉得有意思的东西了，所以你应该学习更多的知识。</p><p>这些内容主要是Web开发相关的内容，包括HTML/CSS/JS（前端页面）、Servlet/JSP（J2EE）以及Mysql（数据库）相关的知识。</p><p>它们的学习顺序应该是从前到后，因此最先学习的应该是HTML/CSS/JS（前端页面），这部分内容你可以去上面的那个runoob网站上找。你可以试着自己写一些页面，当然，你可以尽你最大的努力让它变得最漂亮。这部分内容对于后端Java来说，理论上不是特别重要，但至少要达到可以自己写出一些简单页面的水平。</p><p>接下来，你需要学习的是Servlet/JSP（J2EE）部分，这部分是Java后端开发必须非常精通的部分，因此这部分是这三部分中最需要花精力的，而且这个时候，你要学会使用开发工具，而不能再使用记事本了，可以选择eclipse。</p><p>当你下载安装好eclipse以后，请视频中的教程一步一步去学习，一定要多动手。关于Servlet/Jsp部分视频的选择，业界比较认可马士兵的视频，因此推荐给大家。当然了，LZ本人并没有看过他的视频，所以不好说的太绝对，如果大家自己有更好的选择，可以坚持自己的，不要被LZ干扰。</p><p>原本LZ也是打算出教学视频的，但是由于时间问题，还是决定放弃了。但是如果你看视频的过程中遇到了问题，欢迎来LZ的交流群提问，或者去斗鱼观看LZ的直播提出你的问题，直播地址和群号都在LZ的个人博客左侧。</p><p>最后一步，你需要学会使用数据库，mysql是个不错的入门选择，而且Java领域里主流的关系型数据库就是mysql。这部分一般在你学习Servlet/Jsp的时候，就会接触到的，其中的JDBC部分就是数据库相关的部分。你不仅要学会使用JDBC操作数据库，还要学会使用数据库客户端工具，比如navicat，sqlyog，二选一即可。</p><h3 id="三、开发框架"><a href="#三、开发框架" class="headerlink" title="三、开发框架"></a>三、开发框架</h3><p>当你学会以上内容以后，这个时候你还不足以参加工作，你还需要继续深造。公司里为了提高开发的效率，会使用一些Java Web框架，因此你还需要学习一些开发框架。</p><p>目前比较主流的是SSM框架，即spring、springmvc、mybatis。你需要学会这三个框架的搭建，并用它们做出一个简单的增删改查的Web项目。你可以不理解那些配置都是什么含义，以及为什么要这么做，这些留着后面你去了解。但你一定要可以快速的利用它们三个搭建出一个Web框架，你可以记录下你第一次搭建的过程，相信我，你一定会用到的。</p><p>还要提一句的是，你在搭建SSM的过程中，可能会经常接触到一个叫maven的工具。这个工具也是你以后工作当中几乎是必须要使用的工具，所以你在搭建SSM的过程中，也可以顺便了解一下maven的知识。在你目前这个阶段，你只需要在网络上了解一下maven基本的使用方法即可，一些高端的用法随着你工作经验的增加，会逐渐接触到的。</p><p>关于学习SSM框架的地址给大家推荐一个，这里面有视频，大家可以去观看。</p><h3 id="四、找工作"><a href="#四、找工作" class="headerlink" title="四、找工作"></a>四、找工作</h3><p>当你完成开发框架的学习以后，你就该找工作了，在校的找实习，毕业的找全职。与此同时，在找工作的同时，你不应该停下你的学习，准确的说，是你在以后都不能停下学习。</p><p>上面这些内容你只是囫囵吞枣的学会了使用，你可以逐步尝试着去了解更多的东西，网络是你最重要的老师。</p><h3 id="第一部分：对于参加工作一年以内的同学。"><a href="#第一部分：对于参加工作一年以内的同学。" class="headerlink" title="第一部分：对于参加工作一年以内的同学。"></a>第一部分：对于参加工作一年以内的同学。</h3><p>恭喜你，这个时候，你已经拥有了一份Java的工作。这个阶段是你成长极快的阶段，而且你可能会经常加班。</p><p>但是加班不代表你就可以松懈了，永远记得LZ说的那句话，从你入行那一刻起，你就要不停的学习。在这一年里，你至少需要看完《Java编程思想》这本书。这本书的内容是帮助你对于Java有一个更加深入的了解，是Java基础的升级版。</p><p>这本书很厚，当初看这本书，LZ花了整整三个月。正常速度的话，应该可以在半年左右看完。LZ这里不要求过高，只要你在一年以内把这本书看完即可。当然了，LZ所说的看完，是充分吸收，而不是读一遍就完事了，因此有些内容你可能会看不止一遍。</p><p>总而言之，这个阶段的核心学习思想就是，在工作中实践，并且更加深入的了解Java基础。</p><h3 id="第二部分：对于参加工作1年到2年的同学。"><a href="#第二部分：对于参加工作1年到2年的同学。" class="headerlink" title="第二部分：对于参加工作1年到2年的同学。"></a>第二部分：对于参加工作1年到2年的同学。</h3><p>这部分时间段的同学，已经对Java有了一个更加深入的了解。但是对于面向对象的体会可能还不够深刻，编程的时候还停留在完成功能的层次，很少会去考虑设计的问题。</p><p>于是这个时候，设计模式就来了。LZ当时看的是《大话设计模式》这本书，并且写了完整版的设计模式博客。因此，LZ要求大家，最多在你工作一年的时候，必须开始写博客，而设计模式就是你博客的开端。</p><p>请记住，LZ所提的基本都是最低要求，因此不要有任何松懈的心理，否则五年后，你不要去羡慕别人高于你的工资，也不要去羡慕别人进入了某公司。</p><p>这一年，你必须对于设计模式了如指掌，《大话设计模式》可以作为你的开端。当然了，你也可以去看LZ的个人博客去学习，地址请点击这里。</p><p>此外，设计模式并不是你这一年唯一的任务，你还需要看一些关于代码编写优化的书。比如《重构 改善既有代码的设计》，《effective java》。</p><p>总而言之，这个阶段，你的核心任务就是提高你的代码能力，要能写出一手优雅的代码。</p><h3 id="第三部分：对于参加工作2年到3年的同学"><a href="#第三部分：对于参加工作2年到3年的同学" class="headerlink" title="第三部分：对于参加工作2年到3年的同学"></a>第三部分：对于参加工作2年到3年的同学</h3><p>有的同学在这个时候觉得自己已经很牛逼了，于是忍不住开始慢慢松懈。请记住，你还嫩的多。</p><p>这个阶段，有一本书是你必须看的，它叫做《深入理解Java虚拟机》。这本书绝对是Java开发者最重要的书，没有之一。在LZ眼里，这本书的重要性还要高于《Java编程思想》。</p><p>这本书的内容是帮助你全面的了解Java虚拟机，在这个阶段，你一定已经知道Java是运行在JVM之上的。所以，对于JVM，你没有任何理由不了解它。LZ之前有写过JVM系列的知识，可以去看一下，地址请点击这里。</p><p>另外，在过去2年的工作当中，你肯定或多或少接触过并发。这个时候，你应该去更加深入的了解并发相关的知识，而这部分内容，LZ比较推荐《Java并发编程实战》这本书。只要你把这本书啃下来了，并发的部分基本已经了解了十之六七。</p><p>与此同时，这个阶段你要做的事情还远不止如此。这个时候，你应该对于你所使用的框架应该有了更深入的了解，对于Java的类库也有了更深入的了解。因此，你需要去看一些JDK中的类的源码，也包括你所使用的框架的源码。</p><p>这些源码能看懂的前提是，你必须对设计模式非常了解。否则的话，你看源码的过程中，永远会有这样那样的疑问，这段代码为什么要这么写？为什么要定义这个接口，它看起来好像很多余？</p><p>由此也可以看出，这些学习的过程是环环相扣的，如果你任何一个阶段拉下来了，那么你就真的跟不上了，或者说是一步慢步步慢。而且LZ很负责的告诉你，LZ在这个阶段的时候，所学习的东西远多于这里所罗列出来的。因此千万不要觉得你已经学的很多了，LZ所说的这些都只是最低要求，不光是LZ，很多人在这个时间段所学习的内容都远超本文的范围。</p><p>如果你不能跟上节奏的话，若干年后，如果不是程序猿市场还不错的话，你很可能不仅仅是工资比别人低，公司没别人好，而是根本就找不到工作。</p><p>总而言之，这个阶段，你需要做的是深入了解Java底层和Java类库（比如并发那本书就是Java并发包java.concurrent的内容），也就是JVM和JDK的相关内容。而且还要更深入的去了解你所使用的框架，方式比较推荐看源码或者看官方文档。</p><p>另外，还有一种学习的方式，在2年这个阶段，也应该启用了，那就是造轮子。</p><p>不要听信那套“不要重复造轮子”的论调，那是公司为了节省时间成本编造出来的。重复造轮子或许对别人没有价值，因为你造的轮子可能早就有了，而且一般情况下你造出来的轮子还没有现存的好。但是对别人没有价值，不代表对你自己没有价值。</p><p>一个造轮子的过程，是一个从无到有的过程。这个过程可以对你进行系统的锻炼，它不仅考察你的编码能力，还考察你的框架设计能力，你需要让你的轮子拥有足够好的扩展性、健壮性。</p><p>而且在造轮子的过程中，你会遇到各种各样的难题，这些难题往往又是你学习的契机。当你把轮子造好的时候，你一定会发现，其实你自己收获了很多。</p><p>所以，这个阶段，除了上面提到的了解JVM、JDK和框架源码以外，也请你根据别人优秀的源码，去造一个任何你能够想象出来的轮子。</p><h3 id="第四部分：参加工作3年到4年的同学"><a href="#第四部分：参加工作3年到4年的同学" class="headerlink" title="第四部分：参加工作3年到4年的同学"></a>第四部分：参加工作3年到4年的同学</h3><p>这个阶段的同学，提升已经是很难了，而且这个阶段的学习往往会比较多样化。</p><p>因为在前3年的过程中，你肯定或多或少接触过一些其它的技术，比如大数据、分布式缓存、分布式消息服务、分布式计算、软负载均衡等等。这些技术，你能精通任何一项，都将是你未来面试时巨大的优势，因此如果你对某一项技术感兴趣的话，这个时候可以深入去研究一下。这项技术不一定是你工作所用到的，但一定是相关的。</p><p>而且在研究一门新技术时，切忌朝三暮四。有的同学今天去整整大数据，搞搞Hadoop、hbase一类的东西。过不了一段时间，就觉得没意思，又去研究分布式缓存，比如redis。然后又过不了一段时间，又去研究分布式计算，比如整整Mapreduce或者storm。</p><p>结果到最后，搞得自己好像什么都会一样，在简历上大言不惭的写上大数据、分布式缓存、分布式计算都了解，其实任何一个都只是浮于表面。到时候面试官随便一问，就把你给识破了。</p><p>一定要记住，作为一个程序猿，平日里所接触的技术可能会很多，但是想要让一门技术成为你的优势，那么一定是你对这门技术的了解强过绝大多数人才行。</p><p>因此在这个阶段，你就不能再简单的去学习前3年的内容了，虽然前面的学习如果还不够深入的话依旧要继续，但这个时候你应该更多的考虑建立你的优势，也可以称为差异性。</p><p>差异性相信不难理解，就是让你自己变得与众不同。你前面三年的学习足够你成为一名基本合格的Java开发者，但你离成为一名优秀的Java开发者还有很大的距离。</p><p>所谓优秀，即能别人所不能。而你前三年所学习的内容，是很多做过几年的Java开发都能够掌握的。那么为了让自己有差异性，你就需要另辟蹊径，找一个方向深入研究下去，以期在将来，你能够成为这个领域的专家，比如分布式计算领域的专家，大数据领域的专家，并发领域的专家等等。</p><p>此外，你除了建立你的差异性之外，还要去弥补你基础上的不足，直到现在，LZ都没有提及基础知识。原因是基础是很枯燥无味的，学的太早不仅容易懵逼，而且懵逼的同时还容易产生心理阴影，以至于以后再不想去研究这些基础。但基础又是你深入研究一些领域时所必须掌握的，比如你去研究分布式计算，你不懂算法你玩个毛毛？比如你去做分布式缓存，你对计算机系统的内存不了解，你如何去做缓存？</p><p>如果你的基础本来就非常强，那么恭喜你，相信你在之前的工作中已经充分体会到了这些基础对你的帮助。但LZ相信大部分人的基础都很薄弱，哪怕是科班毕业的人，很多人也不敢说自己当初的基础学的多么强大，比如算法、计算机系统原理、编译原理这些。</p><p>但是每个人时间都是有限的，而且这些基础的书籍每一本读下来，没个一年半载的，还真拿不下来，因此还是要有所抉择的。虽然艺多不压身，但问题是艺多是有代价的，是需要你付出时间和精力的，而LZ个人更赞成在同等代价的情况下获取最大的收获。</p><p>首先，LZ比较推崇的基础书籍有三本，分别是《深入理解计算机系统》，《tcp/ip详解 卷一、二、三》，《数据结构与算法》。其中TCP/IP有三本书，但我们这里把这三本看成是一本大书。</p><p>这三本分别适合三种人，《深入理解计算机系统》比较适合一直从事Java Web开发和APP后端开发工作的人群。《tcp/ip详解 卷一、二、三》比较适合做网络编程的人群，比如你使用netty去开发的话，那么就要对TCP/IP有更深入的了解。而《数据结构与算法》这本书，则比较适合做计算研究工作的人，比如刚才提到的分布式计算。</p><p>另外，LZ要强调的是，这里所说的适合，并不是其它两本对你就没有用。比如你做Java Web和APP后端开发，《tcp/ip详解 卷一、二、三》这本书对你的作用也是很大的。这里只是分出个主次关系而已，你要是时间足够的话，能把三本都精读那当然最好不过了。但如果时间有限的话，那么就先挑对你帮助最大的书去读。</p><p>理论上来讲，这一年你能把这三本其中一本精读下来，就已经非常厉害了。有了基础，有了前面的工作经验，你就可以去开拓属于你的领域了。</p><p>在这一年里，一定要规划好自己的领域，建立好自己的优势，制造出差异性。如果你对自己的领域不够清晰的话，随着你工作的时间日益增多，你接触的技术会越来越多，这个时候，你很容易被淹死在技术的海洋里，看似接触的技术越来越多，会用的也越来越多，但你毫无优势。</p><p>有的同学可能会问，“LZ，我也不知道我的领域是什么啊？怎么办呢？”</p><p>对于这种人，LZ只想说，“卧槽，这还问我？要不干脆我替你学习得了，好不好？”</p><h3 id="第五部分：参加工作4年到5年的同学"><a href="#第五部分：参加工作4年到5年的同学" class="headerlink" title="第五部分：参加工作4年到5年的同学"></a>第五部分：参加工作4年到5年的同学</h3><p>经过前面一年的历练，相信你在自己所钻研的领域已经有了自己一定的见解，这个时候，技术上你应该已经遇到瓶颈了。</p><p>这个时候不要着急提高自己的技术，已经是时候提高你的影响力了，你可以尝试去一些知名的公司去提高你的背景，你可以发表一些文章去影响更多的人。当然，你也可以去Github创建一个属于你的开源项目，去打造自己的产品。这次的开源项目不同于之前的造轮子，你这个时候是真的要去尽量尝试造出来真正对别人有价值的轮子。</p><p>技术学到这个阶段，很容易遇到瓶颈，而且往往达到一定程度后，你再深入下去的收效就真的微乎其微了，除非你是专门搞学术研究的。然而很可惜，大部分程序猿做不到这一步，那是科学家做的事情。</p><p>这个时候提高影响力不仅仅是因为技术上容易遇到瓶颈，更多的是影响力可以给你创造更多的机会。程序猿在某种程度上和明星很像，一个好的电视剧和电影就可以成就一批明星，程序猿有的时候也是，一个好的项目就可以成就一群程序猿。</p><p>比如国内几个脍炙人口的项目，像淘宝、支付宝、QQ、百度、微信等等。这每一个项目，都成就了一批程序猿。LZ敢说，这里面任何一个项目，如果你是它的核心开发，光是这样一个Title，就已经是你非常大的优势。更何况还不止如此，Title说到底也是个名头，更重要的是，这种项目在做的时候，对你的历练一定也是非常给力的。</p><p>而你如果想要参与这样的项目，除了靠运气之外，影响力也是很重要的一个手段。比如你在分布式计算领域有一定的影响力，那么如果有什么好的关于分布式计算的项目，对方就很可能会邀请你。就算人家不邀请你，你自己主动去面试的时候，对方如果知道你在这个领域的影响力，也肯定会起到很大的作用，而这个作用，甚至可能会超过你现在的技术能力。</p><p>所以，在这个阶段，你最大的任务是提高自己的影响力，为自己未来的十年工作生涯那一天做准备。如果你能够靠你的影响力和以前积累的技术，参与到一个伟大的项目当中，那么你后面的五年也就有着落了。</p><p>当然了，LZ现在满打满算，做程序猿也就4年半不到，因此关于4年到5年这一部分，LZ的见解不一定是对的，就算是对的，也不一定是适合任何人的。所以，希望大家自己有的判断力，去决定到底该如何度过这一年。</p><p>结语</p><p>本文到此就基本结束了，整篇文章很长，但其实主要就说了两部分内容，一个是社招面试的准备，一个是Java生涯的学习。</p><p>关于这两部分，LZ已经给出了自己的见解，但是还是那句话，每个人吸收知识的时候，都要有抽取精华，去除糟粕的能力。LZ所说的，可能有些是对的，有些是错的，有些是适合你的，有些是不太适合你的，你要自己能够判断。</p><p>其实你在生活和工作当中也是一样的，你身边的人形形色色，有的人你喜欢，有的人你很讨厌。但其实你喜欢的人也有缺点，你讨厌的人也有优点。你要学会从你讨厌的人身上学会他的优点，千万不要一棒子打死，这只会让你失去很多学习成长的机会。</p><p>好了，说了这么多了，就到此为止吧，希望本文可以帮助到作为程序猿或即将成为程序猿的你。</p><blockquote><p>转自左潇龍博客<a href="http://www.zuoxiaolong.com/blog/article.ftl?id=184" target="_blank" rel="noopener">http://www.zuoxiaolong.com/blog/article.ftl?id=184</a></p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;h3 id=&quot;引言&quot;&gt;&lt;a href=&quot;#引言&quot; class=&quot;headerlink&quot; title=&quot;引言&quot;&gt;&lt;/a&gt;引言&lt;/h3&gt;&lt;p&gt;其实本来真的没打算写这篇文章，主要是LZ得记忆力不是很好，不像一些记忆力强的人，面试完以后，几乎能把自己和面试官的对话都给记下来。LZ自己当初面试完以后，除了记住一些聊过的知识点以外，具体的内容基本上忘得一干二净，所以写这篇文章其实是很有难度的。&lt;/p&gt;
&lt;p&gt;但是，最近问LZ的人实在是太多了，为了避免重复回答，给自己省点力气，干脆就在这里统一回复了。&lt;/p&gt;
&lt;p&gt;其实之前LZ写过一篇文章，但是那篇文章更多的是在讨论“面试前该不该刷题”这个话题，而这篇文章将会更加聚焦在面试前如何准备，以及工作当中如何学习这个话题上，而且会尽量写出一些干货。&lt;br&gt;
    
    </summary>
    
      <category term="网络转载" scheme="http://blog.lyq3.com/categories/%E7%BD%91%E7%BB%9C%E8%BD%AC%E8%BD%BD/"/>
    
    
      <category term="JAVA" scheme="http://blog.lyq3.com/tags/JAVA/"/>
    
      <category term="学习" scheme="http://blog.lyq3.com/tags/%E5%AD%A6%E4%B9%A0/"/>
    
      <category term="励志" scheme="http://blog.lyq3.com/tags/%E5%8A%B1%E5%BF%97/"/>
    
  </entry>
  
  <entry>
    <title>SpringMVC起步-构建Web应用程序</title>
    <link href="http://blog.lyq3.com/2017/09/04/Spring_in_Action/SpringMVC-1/"/>
    <id>http://blog.lyq3.com/2017/09/04/Spring_in_Action/SpringMVC-1/</id>
    <published>2017-09-04T14:19:58.000Z</published>
    <updated>2018-04-30T10:44:26.098Z</updated>
    
    <content type="html"><![CDATA[<h3 id="1-SpringMVC流程图"><a href="#1-SpringMVC流程图" class="headerlink" title="1.SpringMVC流程图"></a>1.SpringMVC流程图</h3><p>先来看看流程图，从浏览器发送请求到响应完成经过的流程</p><p><img src="/images/Spring_in_Action/mvc1.png" alt="SpringMVC--小小牛博客"></p><h3 id="2-配置DispatcherServlet"><a href="#2-配置DispatcherServlet" class="headerlink" title="2.配置DispatcherServlet"></a>2.配置DispatcherServlet</h3><blockquote><p>DispatcherServlet是Spring MVC的核心所有请求都将经过它。<br><a id="more"></a></p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SpitterWebInitializer</span> <span class="keyword">extends</span> <span class="title">AbstractAnnotationConfigDispatcherServletInitializer</span> </span>&#123;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">protected</span> Class&lt;?&gt;[] getRootConfigClasses() &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> Class&lt;?&gt;[] &#123; RootConfig.class &#125;;<span class="comment">//这里用RootConfig类来配置根上下文（里面是一些Bean）</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">protected</span> Class&lt;?&gt;[] getServletConfigClasses() &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> Class&lt;?&gt;[] &#123; WebConfig.class &#125;;<span class="comment">//这里用WebConfig类来配置Servlet上下文（里面是一些Bean）</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">protected</span> String[] getServletMappings() &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> String[] &#123; <span class="string">"/"</span> &#125;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>AbstractAnnotationConfigDispatcherServletInitializer会同时创建DispatcherServlet和<br>ContextLoaderListener。GetServletConfigClasses()方法返回的带有@Configuration注解的类将会用来定<br>义DispatcherServlet应用上下文中的bean。getRootConfigClasses()方法返回的带<br>有@Configuration注解的类将会用来配置ContextLoaderListener创建的应用上下文中的bean。</p></blockquote><h3 id="3-AbstractAnnotationConfigDispatcherServletInitializer剖析"><a href="#3-AbstractAnnotationConfigDispatcherServletInitializer剖析" class="headerlink" title="3.AbstractAnnotationConfigDispatcherServletInitializer剖析"></a>3.AbstractAnnotationConfigDispatcherServletInitializer剖析</h3><p>在Servlet 3.0环境中，容器会在类路径中查找实现javax.servlet.ServletContainerInitializer接口的类，如果能发现的话，就会用它来配置Servlet容器。</p><p>Spring提供了这个接口的实现，名为SpringServletContainerInitializer，这个类反过来又会查找实现WebApplicationInitializer的类并将配置的任务交给它们来完成。Spring 3.2引入了一个便利的WebApplicationInitializer基础实现，也就是AbstractAnnotationConfigDispatcherServletInitializer。因为我们的Spittr-WebAppInitializer扩展了AbstractAnnotationConfig DispatcherServlet-Initializer（同时也就实现了WebApplicationInitializer），因此当部署到Servlet 3.0容器中的时候，容器会自动发现它，并用它来配置Servlet上下文.</p><h3 id="4-启用Spring-MVC"><a href="#4-启用Spring-MVC" class="headerlink" title="4.启用Spring MVC"></a>4.启用Spring MVC</h3><blockquote><p>最少配置但勉强能用的Spring MVC配置</p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span> <span class="comment">//配置类注解</span></span><br><span class="line"><span class="meta">@EnableWebMvc</span> <span class="comment">//启用Spring MVC</span></span><br><span class="line"><span class="meta">@ComponentScan</span>(<span class="string">"spittr.web"</span>) <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">WebConfig</span> <span class="keyword">extends</span> <span class="title">WebMvcConfigurerAdapter</span> </span>&#123;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> ViewResolver <span class="title">viewResolver</span><span class="params">()</span> </span>&#123;<span class="comment">//配置JSP视图解析器</span></span><br><span class="line">  InternalResourceViewResolver resolver = <span class="keyword">new</span> InternalResourceViewResolver();</span><br><span class="line">  resolver.setPrefix(<span class="string">"/WEB-INF/views/"</span>);</span><br><span class="line">  resolver.setSuffix(<span class="string">".jsp"</span>);</span><br><span class="line">  <span class="keyword">return</span> resolver;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//配置静态资源的处理</span></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">configureDefaultServletHandling</span><span class="params">(DefaultServletHandlerConfigurer configurer)</span> </span>&#123;</span><br><span class="line">  configurer.enable();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">addResourceHandlers</span><span class="params">(ResourceHandlerRegistry registry)</span> </span>&#123;</span><br><span class="line">  <span class="comment">// TODO Auto-generated method stub</span></span><br><span class="line">  <span class="keyword">super</span>.addResourceHandlers(registry);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="5-AbstractAnnotationConfigDispatcherServletInitializer额外配置"><a href="#5-AbstractAnnotationConfigDispatcherServletInitializer额外配置" class="headerlink" title="5.AbstractAnnotationConfigDispatcherServletInitializer额外配置"></a>5.AbstractAnnotationConfigDispatcherServletInitializer额外配置</h3><p>上面我们继承了AbstractAnnotationConfigDispatcherServletInitializer并重写了三个方法来配置上下文，但实际上还有更多的方法可以进行重载，从而实现额外的配置。</p><p>此类的方法之一就是customizeRegistration()。在AbstractAnnotationConfigDispatcherServletInitializer<br>将DispatcherServlet注册到Servlet容器中之后，就会调用customizeRegistration()，并将Servlet注册后得到的<br>Registration.Dynamic传递进来。通过重载customizeRegistration()方法，我们可以对DispatcherServlet进行额外的配置</p><ul><li>使用customizeRegistration()配置文件上传</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">customizeRegistration</span><span class="params">(Dynamic registration)</span></span>&#123;</span><br><span class="line">  registration.setMultipartConfig (</span><br><span class="line">  <span class="keyword">new</span> MultipartConfigElement(<span class="string">"/tem/uploads"</span>)</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="6-添加其他的Servlet和Filter"><a href="#6-添加其他的Servlet和Filter" class="headerlink" title="6.添加其他的Servlet和Filter"></a>6.添加其他的Servlet和Filter</h3><p>按照AbstractAnnotationConfigDispatcherServletInitializer的定义，它会创建DispatcherServlet和<br>ContextLoaderListener。但是，如果你想注册其他的Servlet、Filter或Listener的话，那该怎么办呢？<br>基于Java的初始化器（initializer）的一个好处就在于我们可以定义任意数量的初始化器类。因此，如果我们想往Web容器中注册其他组件的话，只需创建一个新的初始化器就可以了。最简单的方式就是实现Spring的WebApplicationInitializer接口。</p><p><img src="/images/Spring_in_Action/mvc5.png" alt="SpringMVC--小小牛博客"></p>]]></content>
    
    <summary type="html">
    
      &lt;h3 id=&quot;1-SpringMVC流程图&quot;&gt;&lt;a href=&quot;#1-SpringMVC流程图&quot; class=&quot;headerlink&quot; title=&quot;1.SpringMVC流程图&quot;&gt;&lt;/a&gt;1.SpringMVC流程图&lt;/h3&gt;&lt;p&gt;先来看看流程图，从浏览器发送请求到响应完成经过的流程&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/Spring_in_Action/mvc1.png&quot; alt=&quot;SpringMVC--小小牛博客&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;2-配置DispatcherServlet&quot;&gt;&lt;a href=&quot;#2-配置DispatcherServlet&quot; class=&quot;headerlink&quot; title=&quot;2.配置DispatcherServlet&quot;&gt;&lt;/a&gt;2.配置DispatcherServlet&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;DispatcherServlet是Spring MVC的核心所有请求都将经过它。&lt;br&gt;
    
    </summary>
    
      <category term="看书笔记" scheme="http://blog.lyq3.com/categories/%E7%9C%8B%E4%B9%A6%E7%AC%94%E8%AE%B0/"/>
    
      <category term="Spring in Action" scheme="http://blog.lyq3.com/categories/%E7%9C%8B%E4%B9%A6%E7%AC%94%E8%AE%B0/Spring-in-Action/"/>
    
    
      <category term="Spring" scheme="http://blog.lyq3.com/tags/Spring/"/>
    
      <category term="Spring in Action" scheme="http://blog.lyq3.com/tags/Spring-in-Action/"/>
    
      <category term="SpringMVC" scheme="http://blog.lyq3.com/tags/SpringMVC/"/>
    
  </entry>
  
  <entry>
    <title>Spring in Anction:Spring AOP 小记</title>
    <link href="http://blog.lyq3.com/2017/08/22/Spring_in_Action/Spring-AOP/"/>
    <id>http://blog.lyq3.com/2017/08/22/Spring_in_Action/Spring-AOP/</id>
    <published>2017-08-22T12:43:28.000Z</published>
    <updated>2018-04-30T10:44:26.098Z</updated>
    
    <content type="html"><![CDATA[<h3 id="1-什么是AOP（面向切面编程）"><a href="#1-什么是AOP（面向切面编程）" class="headerlink" title="1.什么是AOP（面向切面编程）"></a>1.什么是AOP（面向切面编程）</h3><p><img src="/images/Spring_in_Action/aop.png" alt="AOP"></p><div class="note success"><p>不扯那些概念的东西，简单说来AOP是OOP的一个补充，AOP可以在程序运行期追加一些公用的功能，比如权限判断，日志记录，这些功能都是项目需要的，但是又不能每个地方都调用，这样无疑增加了代码的复杂度和工作量，我们可以将这些分散在系统中的公用代码集中于一个地方并通过aop技术应用于系统各个地方。</p></div><h3 id="2-AOP术语"><a href="#2-AOP术语" class="headerlink" title="2. AOP术语"></a>2. AOP术语</h3><ul><li>切点：我们需要插入这些公用功能的点？比如哪些类？哪些方法？等等（何处调用）</li><li>切面：这些公用功能代码（调用什么）</li><li>通知：在什么时候调用？<a id="more"></a><h3 id="3-通知分类"><a href="#3-通知分类" class="headerlink" title="3.通知分类"></a>3.通知分类</h3></li><li>前置通知（Before）：在目标方法调用前调用</li><li>后置通知（After）：在目标方法调用后调用</li><li>返回通知（AfterReturning）：在目标方法成功执行后调用</li><li>异常通知（AfterThrowing）：在目标方法执行失败抛异常后调用</li><li>环绕通知（Around）：将目标方法包裹，由你决定什么时候调用（最强大，最常用）<h3 id="3-Spring-AOP"><a href="#3-Spring-AOP" class="headerlink" title="3.Spring AOP"></a>3.Spring AOP</h3>Spring 通过生成代理类将目标对象包裹从而实现AOP，调用者调用目标方法时，其实是在调用代理类，再由代理类<br>调用目标方法，从而控制对目标方法的调用，Spring只支持最细方法级别的连接点，如果需要实现更细粒度的控制<br>则需要借助专业的AOP框架AspectJ，可以控制字段，构造方法等更细的东西，不过需要学习新的语法。</li></ul><p><img src="/images/Spring_in_Action/aop2.png" alt="AOP"></p><h3 id="4-Spring-切点表达式"><a href="#4-Spring-切点表达式" class="headerlink" title="4.Spring 切点表达式"></a>4.Spring 切点表达式</h3><p>可以通过这些表达式配置切点</p><p><img src="/images/Spring_in_Action/aop3.png" alt="AOP"></p><p>上面是Spring支持的AspectJ 切点表达式。除execution是增加切点范围的外，其他都是用于缩小范围</p><h3 id="5-编写切点"><a href="#5-编写切点" class="headerlink" title="5.编写切点"></a>5.编写切点</h3><p><img src="/images/Spring_in_Action/aop5.png" alt="AOP"></p><h3 id="6-编写切面"><a href="#6-编写切面" class="headerlink" title="6.编写切面"></a>6.编写切面</h3><p>用注解@Aspect表示一个切面，注解用于类上</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Aspect</span> <span class="comment">//表示是切面</span></span><br><span class="line"><span class="meta">@Component</span> <span class="comment">//Bean注入Spring</span></span><br><span class="line"><span class="meta">@Order</span>(<span class="number">1</span>)<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">TicketLoginAspect</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> HttpServletRequest request;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 切点</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">       <span class="comment">//表示作用于TicketLogin注解</span></span><br><span class="line">    <span class="meta">@Pointcut</span>(<span class="string">"@annotation(com.cnct.webchat.common.annotation.TicketLogin)"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">loginPointCut</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">//扩展作用范围，不用在每个@Around后写很长的表达式了，只需写方法名即可，像下面</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Around</span>(<span class="string">"loginPointCut()"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">around</span><span class="params">(ProceedingJoinPoint point)</span> <span class="keyword">throws</span> Throwable </span>&#123;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><blockquote><p>写完切面后要开启Spring aop自动代理才能生效，如果使用JavaConfig的话，在配置类上使用<br>@EnableAspectJ-AutoProxy即可，如果用XML配置则加入<a href="aop:aspectJ-autoproxy/" target="_blank" rel="noopener">aop:aspectJ-autoproxy/</a></p></blockquote><h3 id="7-通过注解为目标类增加新功能（方法）"><a href="#7-通过注解为目标类增加新功能（方法）" class="headerlink" title="7.通过注解为目标类增加新功能（方法）"></a>7.通过注解为目标类增加新功能（方法）</h3><p>既然AOP通过代理实现，那么就可以为目标对象增加新的代理方法。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Aspect</span> <span class="comment">//定义切面</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestAop</span></span>&#123;</span><br><span class="line">  <span class="meta">@DeclareParents</span>(value = <span class="string">"com.lyq3.NewFunctionInterface+"</span>,</span><br><span class="line">                  defaultImpl = NewFunctionImpl.class</span><br><span class="line">  )</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> Abc abc;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>value 指定那种类型的Bean要引入，（后面的“+”号表示所有的子类型而不是本身）</li><li>defaultImpl 指定引入功能的实现类</li><li>@DeclareParents 所标注的静态属性指明要引入的接口，也就是要为Abc接口增加新功能</li></ul><h3 id="8-注意：踩坑记"><a href="#8-注意：踩坑记" class="headerlink" title="8.注意：踩坑记"></a>8.注意：踩坑记</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Aspect</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="meta">@Order</span>(<span class="number">2</span>)<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">HandleRecordAspect</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> HandleRecordMapper handleRecordMapper;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> HttpSession session;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 切点</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Pointcut</span>(<span class="string">"@annotation(com.cnct.webchat.common.annotation.HandleRecord)"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">handlePointCut</span><span class="params">()</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Around</span>(<span class="string">"handlePointCut()"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">around</span><span class="params">(ProceedingJoinPoint point)</span> <span class="keyword">throws</span> Throwable </span>&#123;</span><br><span class="line">        <span class="comment">//先执行方法</span></span><br><span class="line">        Object result = point.proceed();</span><br><span class="line">        <span class="comment">//访问目标方法的参数：</span></span><br><span class="line">        Object[] args = point.getArgs();</span><br><span class="line"></span><br><span class="line">        Signature sig = point.getSignature();</span><br><span class="line">        MethodSignature msig = <span class="keyword">null</span>;</span><br><span class="line">        msig = (MethodSignature) sig;</span><br><span class="line">        Method method = msig.getMethod();</span><br><span class="line"><span class="comment">//        String methodName = msig.getName();//方法名</span></span><br><span class="line">        HandleRecord handleRecord = method.getAnnotation(HandleRecord.class);<span class="comment">//获取注解</span></span><br><span class="line">        <span class="comment">//操作标识（增?删?改?）</span></span><br><span class="line">        <span class="keyword">int</span> handle_status = handleRecord.handle_status();</span><br><span class="line">        String remark = handleRecord.value();</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>上面的代码是我在项目中实现一个存操作纪录的部分代码，咋一看没毛病。@HandleRecord这个自定义注解在Controller层能用，我放到Service层的时候（service有接口和接口实现，注解放实现类方法上）<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">HandleRecord handleRecord = method.getAnnotation(HandleRecord.class);</span><br></pre></td></tr></table></figure></p><p>这句代码是获取不到注解的，也就是说 handleRecord = null;</p><blockquote><p>原因是AOP采用的jdk代理，MethodSignature 转型之后,会丢失子类的方法注解，所以得先获取实现类的方法</p></blockquote><p>修改下代码，就能用了：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//先执行方法</span></span><br><span class="line">        Object result = point.proceed();</span><br><span class="line">        <span class="comment">//访问目标方法的参数：</span></span><br><span class="line">        Object[] args = point.getArgs();</span><br><span class="line"></span><br><span class="line">        Signature sig = point.getSignature();</span><br><span class="line">        MethodSignature msig = <span class="keyword">null</span>;</span><br><span class="line">        msig = (MethodSignature) sig;</span><br><span class="line">        Method method = msig.getMethod();</span><br><span class="line">        <span class="comment">//MethodSignature 转型之后,会丢失子类的方法注解，所以得先获取实现类的方法</span></span><br><span class="line">        <span class="comment">//============================================</span></span><br><span class="line">        Method soruceMethod = point.getTarget().getClass().getMethod(method.getName(), method.getParameterTypes());</span><br><span class="line">        <span class="comment">//=============================================</span></span><br><span class="line">        HandleRecord handleRecord = soruceMethod.getAnnotation(HandleRecord.class);<span class="comment">//获取注解</span></span><br><span class="line">        <span class="comment">//操作标识（增?删?改?）</span></span><br><span class="line">        <span class="keyword">int</span> handle_status = handleRecord.handle_status();</span><br><span class="line">        String remark = handleRecord.value();</span><br></pre></td></tr></table></figure><div class="note success"><p>详情查看Spring in Action 第四版 第四章</p></div>]]></content>
    
    <summary type="html">
    
      &lt;h3 id=&quot;1-什么是AOP（面向切面编程）&quot;&gt;&lt;a href=&quot;#1-什么是AOP（面向切面编程）&quot; class=&quot;headerlink&quot; title=&quot;1.什么是AOP（面向切面编程）&quot;&gt;&lt;/a&gt;1.什么是AOP（面向切面编程）&lt;/h3&gt;&lt;p&gt;&lt;img src=&quot;/images/Spring_in_Action/aop.png&quot; alt=&quot;AOP&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;note success&quot;&gt;&lt;p&gt;不扯那些概念的东西，简单说来AOP是OOP的一个补充，AOP可以在程序运行期追加一些公用的功能，比如权限判断，日志记录，这些功能都是项目需要的，但是又不能每个地方都调用，这样无疑增加了代码的复杂度和工作量，我们可以将这些分散在系统中的公用代码集中于一个地方并通过aop技术应用于系统各个地方。&lt;/p&gt;&lt;/div&gt;
&lt;h3 id=&quot;2-AOP术语&quot;&gt;&lt;a href=&quot;#2-AOP术语&quot; class=&quot;headerlink&quot; title=&quot;2. AOP术语&quot;&gt;&lt;/a&gt;2. AOP术语&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;切点：我们需要插入这些公用功能的点？比如哪些类？哪些方法？等等（何处调用）&lt;/li&gt;
&lt;li&gt;切面：这些公用功能代码（调用什么）&lt;/li&gt;
&lt;li&gt;通知：在什么时候调用？
    
    </summary>
    
      <category term="看书笔记" scheme="http://blog.lyq3.com/categories/%E7%9C%8B%E4%B9%A6%E7%AC%94%E8%AE%B0/"/>
    
      <category term="Spring in Action" scheme="http://blog.lyq3.com/categories/%E7%9C%8B%E4%B9%A6%E7%AC%94%E8%AE%B0/Spring-in-Action/"/>
    
    
      <category term="Spring" scheme="http://blog.lyq3.com/tags/Spring/"/>
    
      <category term="Spring in Action" scheme="http://blog.lyq3.com/tags/Spring-in-Action/"/>
    
      <category term="AOP" scheme="http://blog.lyq3.com/tags/AOP/"/>
    
  </entry>
  
  <entry>
    <title>org.thymeleaf.exceptions.TemplateInputException:template might not exist or might not be accessible by any of the configured Template Resolvers</title>
    <link href="http://blog.lyq3.com/2017/08/20/TemplateInputException/"/>
    <id>http://blog.lyq3.com/2017/08/20/TemplateInputException/</id>
    <published>2017-08-20T04:43:49.000Z</published>
    <updated>2018-04-30T10:44:26.099Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/images/thymeleaf/thymeleaf.png" alt="小小牛博客"></p><h3 id="1-Thymeleaf"><a href="#1-Thymeleaf" class="headerlink" title="1.Thymeleaf"></a>1.Thymeleaf</h3><div class="note success"><p>最近大量使用SpringBoot替代Spring，然而SpringBoot推荐用thymeleaf取代JSP，既然官方都推荐了那就用呗<br>妹的，刚开始用到处都是坑，得踩一段时间才能踩完。</p></div><h3 id="2-格式检测"><a href="#2-格式检测" class="headerlink" title="2.格式检测"></a>2.格式检测</h3><p>Thymeleaf的格式检测有点严格，连html标签没闭合也报错，使用VUE等前端框架时会用很多自定义的标签，会无限报错的，很是不习惯。可以通过引入额外的库解决</p><p>引入依赖：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span>  </span><br><span class="line">       <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>net.sourceforge.nekohtml<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span>  </span><br><span class="line">       <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>nekohtml<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span>  </span><br><span class="line">       <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.9.22<span class="tag">&lt;/<span class="name">version</span>&gt;</span>  </span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><a id="more"></a><p>配置application.yml:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">thymeleaf:</span><br><span class="line">      cache: <span class="keyword">false</span></span><br><span class="line">      mode: LEGACYHTML5</span><br></pre></td></tr></table></figure><p>OK! 不会报错了</p><h3 id="3-Thymeleaf模板布局–报错TemplateInputException"><a href="#3-Thymeleaf模板布局–报错TemplateInputException" class="headerlink" title="3.Thymeleaf模板布局–报错TemplateInputException"></a>3.Thymeleaf模板布局–报错TemplateInputException</h3><p>来说说Thymeleaf的网页布局,jsp布局我们用include引入代码片段组装网页，Thymeleaf也有类似的功能</p><p>但是，TMD报错了：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">org.thymeleaf.exceptions.TemplateInputException: Error resolving template <span class="string">"html/index"</span>, template might not exist or might not be accessible by any of the configured Template Resolvers</span><br><span class="line">at org.thymeleaf.TemplateRepository.getTemplate(TemplateRepository.java:<span class="number">246</span>) ~[thymeleaf-<span class="number">2.1</span>.5.RELEASE.jar:<span class="number">2.1</span>.5.RELEASE]</span><br><span class="line">at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:<span class="number">1104</span>) ~[thymeleaf-<span class="number">2.1</span>.5.RELEASE.jar:<span class="number">2.1</span>.5.RELEASE]</span><br><span class="line">at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:<span class="number">1060</span>) ~[thymeleaf-<span class="number">2.1</span>.5.RELEASE.jar:<span class="number">2.1</span>.5.RELEASE]</span><br><span class="line">at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:<span class="number">1011</span>) ~[thymeleaf-<span class="number">2.1</span>.5.RELEASE.jar:<span class="number">2.1</span>.5.RELEASE]</span><br><span class="line">at org.thymeleaf.spring4.view.ThymeleafView.renderFragment(ThymeleafView.java:<span class="number">335</span>) ~[thymeleaf-spring4-<span class="number">2.1</span>.5.RELEASE.jar:<span class="number">2.1</span>.5.RELEASE]</span><br><span class="line">at org.thymeleaf.spring4.view.ThymeleafView.render(ThymeleafView.java:<span class="number">190</span>) ~[thymeleaf-spring4-<span class="number">2.1</span>.5.RELEASE.jar:<span class="number">2.1</span>.5.RELEASE]</span><br><span class="line">at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:<span class="number">1286</span>) ~[spring-webmvc-<span class="number">4.3</span>.9.RELEASE.jar:<span class="number">4.3</span>.9.RELEASE]</span><br></pre></td></tr></table></figure><p>  这很明显是路径问题，报错原因是Thymeleaf默认模板存放在resources/templates下面，而我在里面多放了一个html文件夹然后</p>  <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">return</span> <span class="string">"html/index"</span>;</span><br></pre></td></tr></table></figure><p>  这样本来没错是可以访问页面的，但是在首页引入其他模板的时候</p>  <figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">th:replace</span>=<span class="string">"header/header :: header"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure><p>  这样Thymeleaf会去默认的resources/templates寻找header/header.html模板所以报错了。</p>  <div class="note success"><p>解决办法：修改默认路径为resources/templates/html</p></div>  <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">thymeleaf:</span><br><span class="line">        prefix: classpath:/templates/html/</span><br></pre></td></tr></table></figure><p>  以前的Controller也要改一下<br>  <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">return</span> <span class="string">"index"</span>;</span><br></pre></td></tr></table></figure></p><p>  这样就可以愉快的写代码了^-^</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;/images/thymeleaf/thymeleaf.png&quot; alt=&quot;小小牛博客&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;1-Thymeleaf&quot;&gt;&lt;a href=&quot;#1-Thymeleaf&quot; class=&quot;headerlink&quot; title=&quot;1.Thymeleaf&quot;&gt;&lt;/a&gt;1.Thymeleaf&lt;/h3&gt;&lt;div class=&quot;note success&quot;&gt;&lt;p&gt;最近大量使用SpringBoot替代Spring，然而SpringBoot推荐用thymeleaf取代JSP，既然官方都推荐了那就用呗&lt;br&gt;妹的，刚开始用到处都是坑，得踩一段时间才能踩完。&lt;/p&gt;&lt;/div&gt;
&lt;h3 id=&quot;2-格式检测&quot;&gt;&lt;a href=&quot;#2-格式检测&quot; class=&quot;headerlink&quot; title=&quot;2.格式检测&quot;&gt;&lt;/a&gt;2.格式检测&lt;/h3&gt;&lt;p&gt;Thymeleaf的格式检测有点严格，连html标签没闭合也报错，使用VUE等前端框架时会用很多自定义的标签，会无限报错的，很是不习惯。可以通过引入额外的库解决&lt;/p&gt;
&lt;p&gt;引入依赖：&lt;/p&gt;
&lt;figure class=&quot;highlight html&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tag&quot;&gt;&amp;lt;&lt;span class=&quot;name&quot;&gt;dependency&lt;/span&gt;&amp;gt;&lt;/span&gt;  &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;       &lt;span class=&quot;tag&quot;&gt;&amp;lt;&lt;span class=&quot;name&quot;&gt;groupId&lt;/span&gt;&amp;gt;&lt;/span&gt;net.sourceforge.nekohtml&lt;span class=&quot;tag&quot;&gt;&amp;lt;/&lt;span class=&quot;name&quot;&gt;groupId&lt;/span&gt;&amp;gt;&lt;/span&gt;  &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;       &lt;span class=&quot;tag&quot;&gt;&amp;lt;&lt;span class=&quot;name&quot;&gt;artifactId&lt;/span&gt;&amp;gt;&lt;/span&gt;nekohtml&lt;span class=&quot;tag&quot;&gt;&amp;lt;/&lt;span class=&quot;name&quot;&gt;artifactId&lt;/span&gt;&amp;gt;&lt;/span&gt;  &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;       &lt;span class=&quot;tag&quot;&gt;&amp;lt;&lt;span class=&quot;name&quot;&gt;version&lt;/span&gt;&amp;gt;&lt;/span&gt;1.9.22&lt;span class=&quot;tag&quot;&gt;&amp;lt;/&lt;span class=&quot;name&quot;&gt;version&lt;/span&gt;&amp;gt;&lt;/span&gt;  &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tag&quot;&gt;&amp;lt;/&lt;span class=&quot;name&quot;&gt;dependency&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
    
    </summary>
    
      <category term="JAVA" scheme="http://blog.lyq3.com/categories/JAVA/"/>
    
    
      <category term="SpringBoot" scheme="http://blog.lyq3.com/tags/SpringBoot/"/>
    
      <category term="Thymeleaf" scheme="http://blog.lyq3.com/tags/Thymeleaf/"/>
    
      <category term="Exception" scheme="http://blog.lyq3.com/tags/Exception/"/>
    
  </entry>
  
  <entry>
    <title>Swagger常用注解与注意事项</title>
    <link href="http://blog.lyq3.com/2017/08/19/swagger-annotation/"/>
    <id>http://blog.lyq3.com/2017/08/19/swagger-annotation/</id>
    <published>2017-08-19T06:24:26.000Z</published>
    <updated>2018-04-30T10:44:26.100Z</updated>
    
    <content type="html"><![CDATA[<h3 id="1-运行环境"><a href="#1-运行环境" class="headerlink" title="1. 运行环境"></a>1. 运行环境</h3><blockquote><p>接上一篇文章 <a href="/2017/08/18/SpringBoot_Swagger">SpringBoot集成Swagger生成在线API文档</a><br>SpringBoot + Swagger2 + swagger-bootstrap-ui</p></blockquote><hr><p>我们这里的UI使用的是第三方的swagger-bootstrap-ui 难免有一些BUg但是不影响使用</p><p><img src="/images/swagger/swagger-an.png" alt="文档页面展示"><br><a id="more"></a></p><h3 id="2-常用注解"><a href="#2-常用注解" class="headerlink" title="2.常用注解"></a>2.常用注解</h3><ul><li>Api –类注解</li><li>ApiModel</li><li>ApiModelProperty</li><li>ApiOperation</li><li>ApiParam</li><li>ApiResponse</li><li>ApiResponses</li><li>ResponseHeader</li></ul><h3 id="3-注解使用案列"><a href="#3-注解使用案列" class="headerlink" title="3.注解使用案列"></a>3.注解使用案列</h3><h5 id="▷-Api-注解"><a href="#▷-Api-注解" class="headerlink" title="▷ @Api 注解"></a>▷ @Api 注解</h5><blockquote><p>该注解放在Controller类上用于描述一类接口，最终会在页面生成描述列表</p></blockquote><table><thead><tr><th>属性名称</th><th style="text-align:left">备注</th></tr></thead><tbody><tr><td>value</td><td style="text-align:left">url的路径值，可写中文描述</td></tr><tr><td>tags</td><td style="text-align:left">如果设置这个值、value的值会被覆盖</td></tr><tr><td>description</td><td style="text-align:left">对api资源的描述</td></tr><tr><td>basePath</td><td style="text-align:left">基本路径可以不配置</td></tr><tr><td>position</td><td style="text-align:left">如果配置多个Api 想改变显示的顺序位置</td></tr><tr><td>produces</td><td style="text-align:left">For example, “application/json, application/xml”</td></tr><tr><td>consumes</td><td style="text-align:left">For example, “application/json, application/xml”</td></tr><tr><td>protocols</td><td style="text-align:left">Possible values: http, https, ws, wss.</td></tr><tr><td>authorizations</td><td style="text-align:left">高级特性认证时配置</td></tr><tr><td>hidden</td><td style="text-align:left">配置为true 将在文档中隐藏</td></tr></tbody></table><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Api</span>(description = <span class="string">"/message"</span>,tags=<span class="string">"Message消息相关接口"</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MessageContoller</span> <span class="keyword">extends</span> <span class="title">BaseContoller</span></span>&#123;</span><br><span class="line">  <span class="comment">//省略</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>效果：<br><img src="/images/swagger/swagger-an2.png" alt="文档页面展示"></p><h5 id="▷-ApiOperation-注解"><a href="#▷-ApiOperation-注解" class="headerlink" title="▷ @ApiOperation 注解"></a>▷ @ApiOperation 注解</h5><blockquote><p>该注解放在Controller的方法上用于描述具体的接口</p></blockquote><table><thead><tr><th>属性名称</th><th style="text-align:left">备注     </th></tr></thead><tbody><tr><td>value</td><td style="text-align:left">url的路径值 ，可写中文描述</td></tr><tr><td>tags</td><td style="text-align:left">如果设置这个值、value的值会被覆盖</td></tr><tr><td>description</td><td style="text-align:left">对api资源的描述</td></tr><tr><td>basePath</td><td style="text-align:left">基本路径可以不配置</td></tr><tr><td>position</td><td style="text-align:left">如果配置多个Api 想改变显示的顺序位置</td></tr><tr><td>produces</td><td style="text-align:left">For example, “application/json, application/xml”</td></tr><tr><td>consumes</td><td style="text-align:left">For example, “application/json, application/xml”</td></tr><tr><td>protocols</td><td style="text-align:left">Possible values: http, https, ws, wss.</td></tr><tr><td>authorizations</td><td style="text-align:left">高级特性认证时配置</td></tr><tr><td>hidden</td><td style="text-align:left">配置为true 将在文档中隐藏</td></tr><tr><td>response</td><td style="text-align:left">返回的对象</td></tr><tr><td>responseContainer</td><td style="text-align:left">这些对象是有效的 “List”, “Set” or “Map”.，其他无效</td></tr><tr><td>httpMethod</td><td style="text-align:left">“GET”, “HEAD”, “POST”, “PUT”, “DELETE”, “OPTIONS” and “PATCH”</td></tr><tr><td>code</td><td style="text-align:left">http的状态码 默认 200</td></tr><tr><td>extensions</td><td style="text-align:left">扩展属性</td></tr></tbody></table><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@ApiOperation</span>(value = <span class="string">"/test"</span>, notes = <span class="string">"发送消息"</span>)</span><br><span class="line"><span class="function"><span class="keyword">public</span> Map&lt;String, Object&gt; <span class="title">sendMessage</span><span class="params">()</span></span>&#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>效果：<br><img src="/images/swagger/swagger-an3.png" alt="文档页面展示"></p><h5 id="▷-ApiParam-注解"><a href="#▷-ApiParam-注解" class="headerlink" title="▷ @ApiParam 注解"></a>▷ @ApiParam 注解</h5><blockquote><p>该注解用于描述参数</p></blockquote><table><thead><tr><th>属性名称</th><th style="text-align:left">备注</th></tr></thead><tbody><tr><td>name</td><td style="text-align:left">属性名称</td></tr><tr><td>value</td><td style="text-align:left">属性值</td></tr><tr><td>defaultValue</td><td style="text-align:left">默认属性值</td></tr><tr><td>allowableValues</td><td style="text-align:left">可以不配置</td></tr><tr><td>required</td><td style="text-align:left">是否属性必填</td></tr><tr><td>access</td><td style="text-align:left">不过多描述</td></tr><tr><td>allowMultiple</td><td style="text-align:left">默认为false</td></tr><tr><td>hidden</td><td style="text-align:left">隐藏该属性</td></tr><tr><td>example</td><td style="text-align:left">举例子</td></tr></tbody></table><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> String <span class="title">test</span><span class="params">(@ApiParam(value = <span class="string">"验证码"</span>,required = <span class="keyword">true</span>)</span> @<span class="title">RequestParam</span><span class="params">(<span class="string">"code"</span>)</span> <span class="keyword">int</span> code,</span></span><br><span class="line"><span class="function">@<span class="title">ApiParam</span><span class="params">(value = <span class="string">"手机号"</span>,required = <span class="keyword">true</span>)</span> @<span class="title">RequestParam</span><span class="params">(<span class="string">"phoneNumber"</span>)</span> String phoneNumber,</span></span><br><span class="line"><span class="function">@<span class="title">ApiParam</span><span class="params">(value = <span class="string">"新密码"</span>,required = <span class="keyword">true</span>)</span> @<span class="title">RequestParam</span><span class="params">(<span class="string">"newPassword"</span>)</span> String newPassword)</span></span><br></pre></td></tr></table></figure><blockquote><p>这里需要注意一点：不知道是swagger-bootstrap-ui的 BUG 还是怎么的，如果只加 @ApiParam 不加@RequestParam注解，前台的接口测试会失效，接口测试时会将参数加入请求的body中而不是参数列表中，所以用了@ApiParam要加@RequestParam指定参数名，大多数时候我们还是用实体类接收参数，就不会出现这种情况</p></blockquote><p>效果：<br><img src="/images/swagger/swagger-an4.png" alt="文档页面展示"></p><p><img src="/images/swagger/swagger-an5.png" alt="文档页面展示"></p><h5 id="▷-ApiModel-注解"><a href="#▷-ApiModel-注解" class="headerlink" title="▷ @ApiModel 注解"></a>▷ @ApiModel 注解</h5><blockquote><p>该注解用于描述实体类，通常与@ApiModelProperty共用<br>                                                |</p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@ApiModel</span>(<span class="string">"工单系统用户信息实体类"</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TicketUser</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="▷-ApiModelProperty-注解"><a href="#▷-ApiModelProperty-注解" class="headerlink" title="▷ @ApiModelProperty 注解"></a>▷ @ApiModelProperty 注解</h5><blockquote><p>该注解用于描述实体类字段</p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@ApiModel</span>(<span class="string">"工单系统用户信息实体类"</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TicketUser</span> </span>&#123;</span><br><span class="line">    <span class="meta">@ApiModelProperty</span>(<span class="string">"用户ID"</span>)</span><br><span class="line">    <span class="keyword">private</span> Integer user_id = <span class="number">0</span>;</span><br><span class="line">    <span class="meta">@ApiModelProperty</span>(<span class="string">"用户名，登录名"</span>)</span><br><span class="line">    <span class="keyword">private</span> String user_name = <span class="string">""</span>;</span><br><span class="line">    <span class="meta">@ApiModelProperty</span>(<span class="string">"密码"</span>)</span><br><span class="line">    <span class="keyword">private</span> String password = <span class="string">""</span>;</span><br><span class="line">    <span class="meta">@ApiModelProperty</span>(<span class="string">"有效表示（Y-有效，N-无效）"</span>)</span><br><span class="line">    <span class="keyword">private</span> String valid_status = <span class="string">""</span>;</span><br><span class="line">    <span class="meta">@ApiModelProperty</span>(<span class="string">"对应的通讯录ID"</span>)</span><br><span class="line">    <span class="keyword">private</span> Integer contact_id=<span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//getter setter 省略</span></span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><p>效果：<br><img src="/images/swagger/swagger-an7.png" alt="文档页面展示"></p><h5 id="▷-ApiResponse注解"><a href="#▷-ApiResponse注解" class="headerlink" title="▷ @ApiResponse注解"></a>▷ @ApiResponse注解</h5><blockquote><p>用于响应配置</p></blockquote><table><thead><tr><th>属性名称</th><th style="text-align:left">备注</th></tr></thead><tbody><tr><td>code</td><td style="text-align:left">http的状态码</td></tr><tr><td>message</td><td style="text-align:left">描述</td></tr><tr><td>response</td><td style="text-align:left">默认响应类 Void</td></tr><tr><td>reference</td><td style="text-align:left">参考ApiOperation中配置</td></tr><tr><td>responseHeaders</td><td style="text-align:left">参考 ResponseHeader 属性配置说明</td></tr><tr><td>responseContainer</td><td style="text-align:left">参考ApiOperation中配置</td></tr></tbody></table><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@RequestMapping</span>(value = <span class="string">"/order"</span>, method = POST)</span><br><span class="line">  <span class="meta">@ApiOperation</span>(value = <span class="string">"Place an order for a pet"</span>, response = Order.class)</span><br><span class="line">  <span class="meta">@ApiResponses</span>(&#123; <span class="meta">@ApiResponse</span>(code = <span class="number">400</span>, message = <span class="string">"Invalid Order"</span>) &#125;)</span><br><span class="line">  <span class="function"><span class="keyword">public</span> ResponseEntity&lt;String&gt; <span class="title">placeOrder</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">      @ApiParam(value = <span class="string">"order placed for purchasing the pet"</span>, required = <span class="keyword">true</span>)</span> Order order) </span>&#123;</span><br><span class="line">    storeData.add(order);</span><br><span class="line">    <span class="keyword">return</span> ok(<span class="string">""</span>);</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><blockquote><p>其他更多注解可以去参考官方文档</p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;h3 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;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;接上一篇文章 &lt;a href=&quot;/2017/08/18/SpringBoot_Swagger&quot;&gt;SpringBoot集成Swagger生成在线API文档&lt;/a&gt;&lt;br&gt;SpringBoot + Swagger2 + swagger-bootstrap-ui&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;p&gt;我们这里的UI使用的是第三方的swagger-bootstrap-ui 难免有一些BUg但是不影响使用&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/swagger/swagger-an.png&quot; alt=&quot;文档页面展示&quot;&gt;&lt;br&gt;
    
    </summary>
    
      <category term="JAVA" scheme="http://blog.lyq3.com/categories/JAVA/"/>
    
    
      <category term="SpringBoot" scheme="http://blog.lyq3.com/tags/SpringBoot/"/>
    
      <category term="Swagger" scheme="http://blog.lyq3.com/tags/Swagger/"/>
    
  </entry>
  
  <entry>
    <title>SpringBoot集成Swagger生成在线API文档,并集成第三方UI</title>
    <link href="http://blog.lyq3.com/2017/08/18/SpringBoot_Swagger/"/>
    <id>http://blog.lyq3.com/2017/08/18/SpringBoot_Swagger/</id>
    <published>2017-08-18T13:37:00.000Z</published>
    <updated>2018-04-30T10:44:26.097Z</updated>
    
    <content type="html"><![CDATA[<h2 id="1-前言"><a href="#1-前言" class="headerlink" title="1.前言"></a>1.前言</h2><p><img src="/images/下决心重构代码时.gif" alt="下决心重构代码时"></p><blockquote><p>今年公司开始尝试前后端分离开发,以前都是前端人员写好静态页面，后端当做模板在后台进行渲染<br>,或者干脆前后台都自己写，现在分离，java人员只提供RESTful的接口，页面交互由前端人员写，这样就会涉及到前后端人员的对接问题，为了提高对接效率，我们在项目中引入了Swagger生成API文档，辅助接口对接<br><a id="more"></a></p></blockquote><h2 id="2-Swagger介绍"><a href="#2-Swagger介绍" class="headerlink" title="2.Swagger介绍"></a>2.Swagger介绍</h2><p>Swagger 是一个规范和完整的框架，用于生成、描述、调用和可视化 RESTful 风格的 Web 服务<br>可以根据后台代码上写的注释生成一个接口展示页面，同时还自带接口调试工具。<br>就像这样：<br><img src="/images/swagger/swagger-ui.jpg" alt="swagger-ui官方版"></p><hr><p>上面是swagger官方UI，感觉不是很好看，可以自己写页面。若果嫌麻烦，网上大神已经写好了，我们现在使用的是<br>swagger-bootstrap-ui，效果图如下：<br><img src="/images/swagger/swagger-ui2.jpg" alt="swagger-bootstrap-ui"></p><hr><p><img src="/images/swagger/swagger-ui3.jpg" alt="swagger-bootstrap-ui"></p><p>感觉好看多了。<br><a href="https://git.oschina.net/xiaoym/swagger-bootstrap-ui" target="_blank" rel="noopener">swagger-bootstrap-ui项目地址</a></p><h2 id="3-SpringBoot集成swagger和swagger-bootstrap-ui"><a href="#3-SpringBoot集成swagger和swagger-bootstrap-ui" class="headerlink" title="3.SpringBoot集成swagger和swagger-bootstrap-ui"></a>3.SpringBoot集成swagger和swagger-bootstrap-ui</h2><h3 id="（1）Maven中加入依赖"><a href="#（1）Maven中加入依赖" class="headerlink" title="（1）Maven中加入依赖"></a>（1）Maven中加入依赖</h3><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line">&lt;!--swagger2支持--&gt;</span><br><span class="line"></span><br><span class="line">  &lt;dependency&gt;</span><br><span class="line"></span><br><span class="line">      &lt;groupId&gt;io.springfox&lt;/groupId&gt;</span><br><span class="line"></span><br><span class="line">      &lt;artifactId&gt;springfox-swagger2&lt;/artifactId&gt;</span><br><span class="line"></span><br><span class="line">      &lt;version&gt;$&#123;springfox.version&#125;&lt;/version&gt;</span><br><span class="line"></span><br><span class="line">  &lt;/dependency&gt;</span><br><span class="line">  &lt;!--这是官方自带的UI--&gt;</span><br><span class="line">  &lt;dependency&gt;</span><br><span class="line"></span><br><span class="line">      &lt;groupId&gt;io.springfox&lt;/groupId&gt;</span><br><span class="line"></span><br><span class="line">      &lt;artifactId&gt;springfox-swagger-ui&lt;/artifactId&gt;</span><br><span class="line"></span><br><span class="line">      &lt;version&gt;$&#123;springfox.version&#125;&lt;/version&gt;</span><br><span class="line"></span><br><span class="line">  &lt;/dependency&gt;</span><br><span class="line"></span><br><span class="line">  &lt;!--第三方swagger UI--&gt;</span><br><span class="line"></span><br><span class="line">  &lt;dependency&gt;</span><br><span class="line"></span><br><span class="line">      &lt;groupId&gt;com.drore.cloud&lt;/groupId&gt;</span><br><span class="line"></span><br><span class="line">      &lt;artifactId&gt;swagger-bootstrap-ui&lt;/artifactId&gt;</span><br><span class="line"></span><br><span class="line">      &lt;version&gt;1.3&lt;/version&gt;</span><br><span class="line"></span><br><span class="line">  &lt;/dependency&gt;</span><br></pre></td></tr></table></figure><h3 id="2-SpringBoot中配置Swagger"><a href="#2-SpringBoot中配置Swagger" class="headerlink" title="(2) SpringBoot中配置Swagger"></a>(2) SpringBoot中配置Swagger</h3><ul><li><h4 id="方式一-采用默认配置-再启动类上加入-EnableSwagger2注解即可"><a href="#方式一-采用默认配置-再启动类上加入-EnableSwagger2注解即可" class="headerlink" title="方式一:采用默认配置,再启动类上加入@EnableSwagger2注解即可"></a>方式一:采用默认配置,再启动类上加入@EnableSwagger2注解即可</h4></li></ul><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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@EnableSwagger</span>2</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">WebchatApplication</span> <span class="keyword">extends</span> <span class="title">SpringBootServletInitializer</span> </span>&#123;</span><br><span class="line"></span><br><span class="line"><span class="keyword">protected</span> <span class="keyword">static</span> Logger logger= LoggerFactory.getLogger(WebchatApplication.class);</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">SpringApplication.run(WebchatApplication.class, args);</span><br><span class="line"></span><br><span class="line">logger.info(<span class="string">"微信管理平台启动成功"</span>);</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>现在可以访问：<a href="http://localhost:8080/项目地址/swagger-ui.html看效果了" target="_blank" rel="noopener">http://localhost:8080/项目地址/swagger-ui.html看效果了</a></p><ul><li><h4 id="方式二：默认配置缺少一些项目描述和权限的配置，可以采用javaConfig的方式做个性化配置"><a href="#方式二：默认配置缺少一些项目描述和权限的配置，可以采用javaConfig的方式做个性化配置" class="headerlink" title="方式二：默认配置缺少一些项目描述和权限的配置，可以采用javaConfig的方式做个性化配置"></a>方式二：默认配置缺少一些项目描述和权限的配置，可以采用javaConfig的方式做个性化配置</h4>1.新建一个配置类SwaggerConf<br>2.代码如下：</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Swagger配置类</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@EnableSwagger</span>2</span><br><span class="line"><span class="meta">@ComponentScan</span>(basePackages = &#123;<span class="string">"com.qq.controller"</span>&#125;)<span class="comment">//controller包路径</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SwaggerConfig</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Docket <span class="title">Api</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> Docket(DocumentationType.SWAGGER_2)</span><br><span class="line"><span class="comment">//                .groupName("Pro")</span></span><br><span class="line"><span class="comment">//                .genericModelSubstitutes(DeferredResult.class)</span></span><br><span class="line"><span class="comment">//                .genericModelSubstitutes(ResponseEntity.class)</span></span><br><span class="line"><span class="comment">//                .useDefaultResponseMessages(false)</span></span><br><span class="line"><span class="comment">//                .forCodeGeneration(true)</span></span><br><span class="line"><span class="comment">//                .pathMapping("/")// base，最终调用接口后会和paths拼接在一起</span></span><br><span class="line">                .select()</span><br><span class="line">                .apis(getPredicate())</span><br><span class="line">                .paths(PathSelectors.any())</span><br><span class="line"><span class="comment">//                .paths(or(regex("/api/.*")))//过滤的接口</span></span><br><span class="line">                .build()</span><br><span class="line"><span class="comment">//                .securitySchemes(securitySchemes())</span></span><br><span class="line"><span class="comment">//                .securityContexts(securityContexts())</span></span><br><span class="line">                .apiInfo(setInfo());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 设置系统信息</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> ApiInfo <span class="title">setInfo</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> ApiInfoBuilder()</span><br><span class="line">                .title(<span class="string">"&lt;b&gt;管理平台API接口文档&lt;/b&gt;"</span>)<span class="comment">//大标题</span></span><br><span class="line">                .description(<span class="string">"（1）方便前后端分离后，前端人员与后端人员的对接。&lt;br/&gt;（2）同时集成接口测试工具，方便后端测试接口&lt;br/&gt;&lt;span style='color:red'&gt;（3）后端人员务必采用Swagger注解写好各接口的描述。&lt;/span&gt;"</span>)<span class="comment">//详细描述</span></span><br><span class="line">                .version(<span class="string">"1.0"</span>)<span class="comment">//版本</span></span><br><span class="line">                .termsOfServiceUrl(<span class="string">"NO terms of service"</span>)</span><br><span class="line">                .contact(<span class="keyword">new</span> Contact(<span class="string">"lyq"</span>, <span class="string">"http://blog.lyq3.com"</span>, <span class="string">"99190092@qq.com"</span>))<span class="comment">//作者</span></span><br><span class="line">                .license(<span class="string">"The Apache License, Version 2.0"</span>)</span><br><span class="line">                .licenseUrl(<span class="string">"http://www.apache.org/licenses/LICENSE-2.0.html"</span>)</span><br><span class="line">                .build();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获取API过滤器</span></span><br><span class="line"><span class="comment">     * 指定需要生成文档的接口</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Predicate&lt;RequestHandler&gt; <span class="title">getPredicate</span><span class="params">()</span></span>&#123;</span><br><span class="line">        Predicate&lt;RequestHandler&gt; predicate = <span class="keyword">new</span> Predicate&lt;RequestHandler&gt;()&#123;</span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">apply</span><span class="params">(RequestHandler input)</span> </span>&#123;</span><br><span class="line">                Class&lt;?&gt; declaringClass = input.declaringClass();</span><br><span class="line">                <span class="keyword">if</span> (declaringClass == BasicErrorController.class)<span class="comment">// 排除</span></span><br><span class="line">                    <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">                <span class="keyword">if</span>(declaringClass.isAnnotationPresent(RestController.class)) <span class="comment">// 被注解的类</span></span><br><span class="line">                    <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">                <span class="keyword">if</span>(input.isAnnotatedWith(ResponseBody.class)) <span class="comment">// 被注解的方法</span></span><br><span class="line">                    <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line">        <span class="keyword">return</span> predicate;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>现在可以访问：<a href="http://localhost:8080/项目地址/swagger-ui.html看效果了" target="_blank" rel="noopener">http://localhost:8080/项目地址/swagger-ui.html看效果了</a></p><h3 id="4-Swagger-注解的使用"><a href="#4-Swagger-注解的使用" class="headerlink" title="4.Swagger 注解的使用"></a>4.Swagger 注解的使用</h3><blockquote><p>配置完成后可以打开页面，也能看到接口，但是缺少接口的具体描述，比如：某个参数到底有什么作用，返回值代表什么的一系列描述是没有的，这就需要我们用Swagger提供的注解描述每一个接口和参数、返回值、状态码等</p></blockquote><h5 id="Swagger注解是重点，单开一篇来讲注解的使用和采坑记"><a href="#Swagger注解是重点，单开一篇来讲注解的使用和采坑记" class="headerlink" title="Swagger注解是重点，单开一篇来讲注解的使用和采坑记"></a>Swagger注解是重点，单开一篇来讲注解的使用和采坑记</h5>]]></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;img src=&quot;/images/下决心重构代码时.gif&quot; alt=&quot;下决心重构代码时&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;今年公司开始尝试前后端分离开发,以前都是前端人员写好静态页面，后端当做模板在后台进行渲染&lt;br&gt;,或者干脆前后台都自己写，现在分离，java人员只提供RESTful的接口，页面交互由前端人员写，这样就会涉及到前后端人员的对接问题，为了提高对接效率，我们在项目中引入了Swagger生成API文档，辅助接口对接&lt;br&gt;
    
    </summary>
    
      <category term="JAVA" scheme="http://blog.lyq3.com/categories/JAVA/"/>
    
    
      <category term="SpringBoot" scheme="http://blog.lyq3.com/tags/SpringBoot/"/>
    
      <category term="Swagger" scheme="http://blog.lyq3.com/tags/Swagger/"/>
    
  </entry>
  
  <entry>
    <title>重写JQuery的Ajax请求实现公用功能，如：权限判断</title>
    <link href="http://blog.lyq3.com/2017/08/17/reAjax/"/>
    <id>http://blog.lyq3.com/2017/08/17/reAjax/</id>
    <published>2017-08-17T13:22:00.000Z</published>
    <updated>2018-04-30T10:44:26.100Z</updated>
    
    <content type="html"><![CDATA[<h3 id="1-前言"><a href="#1-前言" class="headerlink" title="1.前言"></a>1.前言</h3><p><img src="/images/loding.jpg" alt="Loding加载层"></p><blockquote><p>我们做JAVA开发的，管理系统做的比较多，管理系统大多都有一个特点：单页面应用SPA<br>(Single Page Application),单页面程序就会用很多的Ajax异步请求后台查询数据，为了用户体验，我们都会在发送请求的时候加载一个Loding..提示层</p></blockquote><hr><p>又或者是用户掉线了，点击发送请求时会跳回到登录页面重新登录等等，一系列在发送请求时都要做的事情可以重写ajax实现每次发送ajax请求都将执行公用方法<br><a id="more"></a></p><h3 id="2-重写Ajax"><a href="#2-重写Ajax" class="headerlink" title="2.重写Ajax"></a>2.重写Ajax</h3><blockquote><p>新建reAjax.js</p></blockquote><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><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></pre></td><td class="code"><pre><span class="line">$(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line"><span class="keyword">var</span> layer;<span class="comment">//layer弹窗插件，用于页面提示</span></span><br><span class="line"><span class="keyword">var</span> index;</span><br><span class="line">layui.use(<span class="string">'layer'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">layer= layui.layer;</span><br><span class="line">&#125;);</span><br><span class="line">(<span class="function"><span class="keyword">function</span>(<span class="params">$</span>)</span>&#123;</span><br><span class="line"></span><br><span class="line"><span class="comment">//首先备份下jquery的ajax方法</span></span><br><span class="line"><span class="keyword">var</span> _ajax=$.ajax;</span><br><span class="line"></span><br><span class="line"><span class="comment">//重写jquery的ajax方法</span></span><br><span class="line">$.ajax=<span class="function"><span class="keyword">function</span>(<span class="params">opt</span>)</span>&#123;</span><br><span class="line"><span class="comment">//备份opt中error和success方法</span></span><br><span class="line"><span class="keyword">var</span> fn = &#123;</span><br><span class="line">error:<span class="function"><span class="keyword">function</span>(<span class="params">XMLHttpRequest, textStatus, errorThrown</span>)</span>&#123;&#125;,</span><br><span class="line">success:<span class="function"><span class="keyword">function</span>(<span class="params">data, textStatus</span>)</span>&#123;&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span>(opt.error)&#123;</span><br><span class="line">fn.error=opt.error;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span>(opt.success)&#123;</span><br><span class="line">fn.success=opt.success;</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">var</span> _opt = $.extend(opt,&#123;</span><br><span class="line">error:<span class="function"><span class="keyword">function</span>(<span class="params">XMLHttpRequest, textStatus, errorThrown</span>)</span>&#123;</span><br><span class="line"><span class="comment">//错误方法增强处理</span></span><br><span class="line">layer.alert(<span class="string">'连接服务器失败，请稍后重试！'</span>)</span><br><span class="line">fn.error(XMLHttpRequest, textStatus, errorThrown);</span><br><span class="line">&#125;,</span><br><span class="line">success:<span class="function"><span class="keyword">function</span>(<span class="params">data, textStatus</span>)</span>&#123;</span><br><span class="line"><span class="keyword">if</span>(data.code == <span class="number">888</span>)&#123;</span><br><span class="line">layer.alert(<span class="string">'未登录，请重新登录！'</span>,<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line"><span class="built_in">window</span>.location.href=<span class="string">'/login.html'</span>;</span><br><span class="line">&#125;);</span><br><span class="line"><span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(data.code == <span class="number">666</span>)&#123;</span><br><span class="line">layer.alert(<span class="string">'无权限访问！'</span>);</span><br><span class="line"><span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//成功回调方法增强处理</span></span><br><span class="line">fn.success(data, textStatus);</span><br><span class="line">&#125;,</span><br><span class="line">beforeSend:<span class="function"><span class="keyword">function</span>(<span class="params">XHR</span>)</span>&#123;</span><br><span class="line"><span class="comment">//提交前回调方法</span></span><br><span class="line">index = layer.load(<span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">&#125;,</span><br><span class="line">complete:<span class="function"><span class="keyword">function</span>(<span class="params">XHR, TS</span>)</span>&#123;</span><br><span class="line"><span class="comment">//请求完成后回调函数 (请求成功或失败之后均调用)。</span></span><br><span class="line">layer.close(index);</span><br><span class="line">&#125;</span><br><span class="line">&#125;);</span><br><span class="line"><span class="keyword">return</span> _ajax(_opt)</span><br><span class="line">&#125;;</span><br><span class="line">&#125;)(jQuery);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><blockquote><p>传统页面直接引入该js就行，缺点是每个页面都要引一次</p></blockquote><hr><blockquote><p>如果用的webpack打包：直接加入依赖即可</p></blockquote><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">resolve:&#123;</span><br><span class="line">            alias: &#123;</span><br><span class="line">                avalon2: path.resolve(PATHS.node_modulesPath, <span class="string">"avalon2/dist/avalon.js"</span>),</span><br><span class="line">                <span class="comment">//ueditor: path.resolve(PATHS.node_modulesPath, "ueditor/example/public/ueditor"),</span></span><br><span class="line">                bootstrap: path.resolve(PATHS.node_modulesPath, <span class="string">'bootstrap/dist'</span>),</span><br><span class="line">                jquery:  path.resolve(PATHS.libPath,  <span class="string">'jquery-1.12.4.js'</span>),</span><br><span class="line">                <span class="comment">//这里全局引入即可</span></span><br><span class="line">                reAjax:  path.resolve(PATHS.libPath,  <span class="string">'reAjax.js'</span>),</span><br><span class="line">                mmRouter: path.resolve(PATHS.node_modulesPath, <span class="string">"mmRouter/dist/mmRouter.js"</span>)</span><br><span class="line">            &#125;,</span><br><span class="line">            modules: [path.resolve(__dirname, <span class="string">"src/static/lib"</span>), <span class="string">"node_modules"</span>, <span class="string">"src/tpl"</span>]</span><br><span class="line">        &#125;,</span><br><span class="line">        ...</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      &lt;h3 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;/h3&gt;&lt;p&gt;&lt;img src=&quot;/images/loding.jpg&quot; alt=&quot;Loding加载层&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我们做JAVA开发的，管理系统做的比较多，管理系统大多都有一个特点：单页面应用SPA&lt;br&gt;(Single Page Application),单页面程序就会用很多的Ajax异步请求后台查询数据，为了用户体验，我们都会在发送请求的时候加载一个Loding..提示层&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;p&gt;又或者是用户掉线了，点击发送请求时会跳回到登录页面重新登录等等，一系列在发送请求时都要做的事情可以重写ajax实现每次发送ajax请求都将执行公用方法&lt;br&gt;
    
    </summary>
    
      <category term="JavaScript" scheme="http://blog.lyq3.com/categories/JavaScript/"/>
    
    
      <category term="JQuery" scheme="http://blog.lyq3.com/tags/JQuery/"/>
    
      <category term="Ajax" scheme="http://blog.lyq3.com/tags/Ajax/"/>
    
  </entry>
  
</feed>
