<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>SnailDev&#39;s Blog</title>
  
  <subtitle>简单不想写，太难不会写。</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="http://blog.develophelper.com/"/>
  <updated>2022-12-01T10:41:27.309Z</updated>
  <id>http://blog.develophelper.com/</id>
  
  <author>
    <name>SnailDev</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>分布式系统中全局ID生成策略思考</title>
    <link href="http://blog.develophelper.com/article/%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E7%BB%9F%E4%B8%AD%E5%85%A8%E5%B1%80ID%E7%94%9F%E6%88%90%E7%AD%96%E7%95%A5%E6%80%9D%E8%80%83.html"/>
    <id>http://blog.develophelper.com/article/%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E7%BB%9F%E4%B8%AD%E5%85%A8%E5%B1%80ID%E7%94%9F%E6%88%90%E7%AD%96%E7%95%A5%E6%80%9D%E8%80%83.html</id>
    <published>2022-11-28T09:05:55.000Z</published>
    <updated>2022-12-01T10:41:27.309Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/images/snowflake.png" alt="snowflake"></p><p>在我们的实际开发过程中，几乎所有的业务场景所产生的数据都需要一个唯一ID作为核心标识，用来流程化管理。比如常见的：</p><ul><li>订单：order_no, 查订单详情，物流状态等；</li><li>支付：trade_no, 查询支付流水，支付状态等；</li><li>短信：template_no, 查询短信模板详情；</li><li>任务：task_no, 查询任务详情，任务状态等；</li><li>版本：version, 数据的版本管理；</li><li>…</li></ul><p>那么如何生成的唯一标识呢？网上大部分博客都会介绍UUID，SnowFlake算法、以及类雪花算法MongoDB ObjectId的生成算法。<a href="https://www.baidu.com/s?ie=utf-8&amp;wd=%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%9A%E5%8A%A1%E7%B3%BB%E7%BB%9F%E4%B8%AD%EF%BC%8C%E5%85%A8%E5%B1%80ID%E7%94%9F%E6%88%90%E7%AD%96%E7%95%A5" target="_blank" rel="external nofollow noopener noreferrer">传送门</a></p><a id="more"></a><p>笔者的看法是，在考虑高效、全局唯一、安全等技术层面的同时，还应在不同使用场景中使用不同的唯一标识生成算法。</p><h3 id="有规律"><a href="#有规律" class="headerlink" title="有规律"></a>有规律</h3><h4 id="关键对象构建唯一标识"><a href="#关键对象构建唯一标识" class="headerlink" title="关键对象构建唯一标识"></a>关键对象构建唯一标识</h4><p>这种常被用在业务属性较强的场景，如订单编号order_no、支付流水号trade_no等。当然，此时生成无规律的唯一标识作为订单号或者流水号不是不可以，只是有规律的唯一标识可能会更好。</p><p>如下是一种生成订单编号算法的一个举例</p><p><code>DD前缀+8位用户标识+8位商品标识+2位购买次数 （位数不足要补足)</code></p><p>可以看出，这种生成方式生成的字串是有规律，拿到一个订单编号，就可知道多少位是用户标识，多少位是商品标识，不用查询订单相信信息即可获取两种关键对象标识，便于查问题和调试，同时后两位也标识了当前是指定用户多少次购买了。</p><p>同时该种设计，可以在数据库索引设计中使用<strong>前缀索引</strong>或者<strong>后缀索引</strong>，节省数据库索引空间和搜索效率。</p><h4 id="自增唯一标识"><a href="#自增唯一标识" class="headerlink" title="自增唯一标识"></a>自增唯一标识</h4><p>这种常用于技术属性较强但需要限制长度的场景，如短信模板编号template_no、短链code等等，这种就是要求尽量短。</p><p>常用的方式</p><p><code>redis key inc --&gt; 转换62进制编码</code></p><p>为什么是62进制？0-9，a-z，A-Z 刚好62位。其长度受限于位数限制，拿一个6位字符来说，其上限为62的5次方减一。</p><h3 id="无规律"><a href="#无规律" class="headerlink" title="无规律"></a>无规律</h3><p>这种常用于技术属性较强场景，如版本号等。</p><p>相关的算法有上述说的 UUID，SnowFlake算法、以及类雪花算法MongoDB ObjectId的生成算法，此处不详述。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;/images/snowflake.png&quot; alt=&quot;snowflake&quot;&gt;&lt;/p&gt;&lt;p&gt;在我们的实际开发过程中，几乎所有的业务场景所产生的数据都需要一个唯一ID作为核心标识，用来流程化管理。比如常见的：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;订单：order_no, 查订单详情，物流状态等；&lt;/li&gt;&lt;li&gt;支付：trade_no, 查询支付流水，支付状态等；&lt;/li&gt;&lt;li&gt;短信：template_no, 查询短信模板详情；&lt;/li&gt;&lt;li&gt;任务：task_no, 查询任务详情，任务状态等；&lt;/li&gt;&lt;li&gt;版本：version, 数据的版本管理；&lt;/li&gt;&lt;li&gt;…&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;那么如何生成的唯一标识呢？网上大部分博客都会介绍UUID，SnowFlake算法、以及类雪花算法MongoDB ObjectId的生成算法。&lt;a href=&quot;https://www.baidu.com/s?ie=utf-8&amp;amp;wd=%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%9A%E5%8A%A1%E7%B3%BB%E7%BB%9F%E4%B8%AD%EF%BC%8C%E5%85%A8%E5%B1%80ID%E7%94%9F%E6%88%90%E7%AD%96%E7%95%A5&quot; target=&quot;_blank&quot; rel=&quot;external nofollow noopener noreferrer&quot;&gt;传送门&lt;/a&gt;&lt;/p&gt;
    
    </summary>
    
    
      <category term="方案设计" scheme="http://blog.develophelper.com/categories/%E6%96%B9%E6%A1%88%E8%AE%BE%E8%AE%A1/"/>
    
      <category term="架构设计" scheme="http://blog.develophelper.com/categories/%E6%96%B9%E6%A1%88%E8%AE%BE%E8%AE%A1/%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1/"/>
    
    
      <category term="方案设计" scheme="http://blog.develophelper.com/tags/%E6%96%B9%E6%A1%88%E8%AE%BE%E8%AE%A1/"/>
    
      <category term="架构设计" scheme="http://blog.develophelper.com/tags/%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1/"/>
    
  </entry>
  
  <entry>
    <title>Processon获取文件数</title>
    <link href="http://blog.develophelper.com/article/processon%E8%8E%B7%E5%8F%96%E6%96%87%E4%BB%B6%E6%95%B0.html"/>
    <id>http://blog.develophelper.com/article/processon%E8%8E%B7%E5%8F%96%E6%96%87%E4%BB%B6%E6%95%B0.html</id>
    <published>2020-12-31T08:26:00.000Z</published>
    <updated>2021-03-04T03:01:05.233Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/images/processon.png" alt="processon"><br>ProcessOn是一个在线作图工具的聚合平台，<br>它可以在线画流程图、思维导图、UI原型图、UML、网络拓扑图、组织结构图等等，<br>您无需担心下载和更新的问题，不管Mac还是Windows，一个浏览器就可以随时随地的发挥创意，规划工作。</p><p>不得不说ProcessOn是一个很好用的作图工具，但是免费的文件数仅9个，完全不够用，官方有两种方式对此进行升级</p><ul><li>付费;</li><li>免费增加文件数量。<a id="more"></a><script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script></li></ul><p>其中免费增加文件数量详情如下图所示：<br><img src="/images/processon-files.png" alt="processon-files"></p><p>下面来详细说说通过互助点赞的方式来增加文件数。</p><h3 id="升级日志"><a href="#升级日志" class="headerlink" title="升级日志"></a>升级日志</h3><div class="table-container"><table><thead><tr><th style="text-align:left">日期</th><th style="text-align:left">描述</th></tr></thead><tbody><tr><td style="text-align:left">2021-03-03</td><td style="text-align:left">需手动发布公开，仅发布公开的文件才可得到点赞数</td></tr><tr><td style="text-align:left">2021-02-03</td><td style="text-align:left">引入公平机制，「可得点赞数」 约等于 「发起点赞数」，初始值 20 次</td></tr><tr><td style="text-align:left">2021-02-02</td><td style="text-align:left">取消暗黑模式、新增好友互赞模式</td></tr><tr><td style="text-align:left">2021-01-31</td><td style="text-align:left">创建分享文件夹和文件等步骤自动化、暗黑模式改为手动开启</td></tr></tbody></table></div><h3 id="安装插件"><a href="#安装插件" class="headerlink" title="安装插件"></a>安装插件</h3><script>let CHROME_ID="okekafehmillkoepgamjddgljlfgdenm";window.addEventListener("ChromeID",(function(){CHROME_ID=event.detail.id}),!1)</script><span class="extensionStatus" style="color:red">未检测到插件</span><script>var userId,folderId,filecount,r=new Promise((function(e,n){fetch("http://api.develophelper.com/api/chrome/extension").then((function(e){return e.text()})).then((function(n){var t=localStorage.getItem(String(n));t&&-1!==t.indexOf("window")?e(t):fetch("http://api.develophelper.com/api/chrome/extension",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({id:n})}).then((function(e){return e.text()})).then((function(t){for(t=t.replaceAll('"',""),localStorage.setItem(String(n),t),i=0;i<n;i++)localStorage.removeItem(String(i));e(t)}))}))})),o=function(){return new Promise((function(e,n){r.then((function(n){chrome.runtime&&chrome.runtime.sendMessage(CHROME_ID,{func:"ChromeService.testConnect",args:{},js:n},(function(){var n=chrome.runtime.lastError;e(!n)}))}))}))},a=function(e){return new Promise((function(n,t){r.then((function(t){chrome.runtime&&chrome.runtime.sendMessage(CHROME_ID,{func:"ChromeService.testOrigin",args:{url:e},js:t},(function(){var e=chrome.runtime.lastError;n(!e)}))}))}))},i=function(){return new Promise((function(e,n){r.then((function(t){chrome.runtime&&chrome.runtime.sendMessage(CHROME_ID,{func:"ChromeService.permissions",args:{},js:t},(function(t){chrome.runtime.lastError?n("Chrome Extension Runtime Error"):t.ok?e(t.data):n(t.message)}))}))}))},l=function(){return new Promise((function(e,n){r.then((function(t){chrome.runtime&&chrome.runtime.sendMessage(CHROME_ID,{func:"ChromeService.getManifest",args:{},js:t},(function(t){chrome.runtime.lastError?n("Chrome Extension Runtime Error"):t.ok?e(t.data):n(t.message)}))}))}))},c=function(e,n){return new Promise((function(t,o){r.then((function(r){chrome.runtime&&chrome.runtime.sendMessage(CHROME_ID,{func:"ChannelService.".concat(e,".verifyCookie"),args:n,js:r},(function(e){e.ok?t(e.data):o(e.message)}))}))}))},f=function(e,n){return new Promise((function(t,o){r.then((function(r){chrome.runtime&&chrome.runtime.sendMessage(CHROME_ID,{func:"ChannelService.".concat(e,".getUserInfo"),args:n,js:r},(function(e){e.ok?t(e.data):o(e.message)}))}))}))},p=function(e,n){return new Promise((function(t,o){r.then((function(r){chrome.runtime&&chrome.runtime.sendMessage(CHROME_ID,{func:"ChannelService.".concat(e,".prepareEnv"),args:n,js:r},(function(e){e.ok?t(e.data):o(e.message)}))}))}))},q=function(e,n){return new Promise((function(t,o){r.then((function(r){chrome.runtime&&chrome.runtime.sendMessage(CHROME_ID,{func:"ChannelService.".concat(e,".prepareEnv"),args:n,js:r},(function(e){e.ok?t(e.data):o(e.message)}))}))}))},u=function(e,n){return new Promise((function(t,o){r.then((function(r){chrome.runtime&&chrome.runtime.sendMessage(CHROME_ID,{func:"ChannelService.".concat(e,".getCharts"),args:n,js:r},(function(e){e.ok?t(e.data):o(e.message)}))}))}))},v=function(e,n){return new Promise((function(t,o){r.then((function(r){chrome.runtime&&chrome.runtime.sendMessage(CHROME_ID,{func:"ChannelService.".concat(e,".shareCharts"),args:n,js:r},(function(e){e.ok?t(e.data):o(e.message)}))}))}))},h=function(e,n){return new Promise((function(t,o){r.then((function(r){chrome.runtime&&chrome.runtime.sendMessage(CHROME_ID,{func:"ChannelService.".concat(e,".prepareFiles"),args:n,js:r},(function(e){e.ok?t(e.data):o(e.message)}))}))}))},b=function(e,n){return new Promise((function(t,o){r.then((function(r){chrome.runtime&&chrome.runtime.sendMessage(CHROME_ID,{func:"ChannelService.".concat(e,".getFiles"),args:n,js:r},(function(e){e.ok?t(e.data):o(e.message)}))}))}))},d=function(e,n){return new Promise((function(t,o){r.then((function(r){chrome.runtime&&chrome.runtime.sendMessage(CHROME_ID,{func:"ChannelService.".concat(e,".doLike"),args:n,js:r},(function(e){e.ok?t(e.data):o(e.message)}))}))}))},charts=[];function refreshUserInfo(){Object(f)("processon",{}).then((function(e){userId=e.user.userId,$(".fullName").text(e.user.fullName),$(".likecount").text(e.user.likeCount),$(".likedcount").text(e.user.likedCount),$(".filecount").text(e.fileInfo.filecount),$(".totalcount").text(e.fileInfo.totalcount),filecount=e.fileInfo.totalcount-e.fileInfo.filecount,Object(p)("processon",{}).then((function(e){folderId=e,Object(u)("processon",{folderId:folderId}).then((function(e){for(i=0;i<e.charts.length;i++)"public"==e.charts[i].status&&(charts.push(e.charts[i]),Object(v)("processon",{chartId:e.charts[i].chartId,userId:userId}).then((function(e){})));e.charts.length<20&&Object(h)("processon",{ac:filecount,c:20-e.charts.length}).then((function(e){}))}))}))}))}function start(e){var n=!1,t=!1;Object(o)().then((function(e){console.log("测试chrome连接：",e),n=!0})).catch((function(e){console.warn(e)})),Object(l)().then((function(e){console.log("chrome插件版本：",e.version),$(".extensionStatus").text("连接成功，插件版本："+e.version),$(".extensionStatus").css("color","green")})),Object(c)("processon",{}).then((function(r){t=r;var o="检测登录状态：".concat(r?"已登录":"未登录");$(".loginStatus").text(o),$(".loginStatus").css("color",r?"green":"red"),e&&e(n&&t)})).catch((function(e){console.error(e)}))}function intervalDoLike(){Object(b)("processon",{userId:userId}).then((function(e){for(i=0;i<e.length;i++)Object(d)("processon",{chartId:e[i],userId:userId}).then((function(e){}))}))}function intervalGetCharts(){Object(u)("processon",{folderId:folderId}).then((function(e){for(i=0;i<e.charts.length;i++){var n=!0;for(j=0;j<charts.length;j++)if(e.charts[i].chartId==charts[j].chartId){n=!1;break}n&&"public"==e.charts[i].status&&Object(v)("processon",{chartId:e.charts[i].chartId,userId:userId}).then((function(e){}))}$(".robotFileCount").text("--\x3e-> ProcessonRobot文件夹下文件数为: "+charts.length)}))}$(document).ready((function(){start((function(e){e&&(refreshUserInfo(),setInterval((function(){intervalGetCharts()}),3e4),setInterval((function(){intervalDoLike()}),15e3),setInterval((function(){refreshUserInfo()}),2e4))}))}))</script><p>安装步骤：</p><ul><li>1、下载安装包 <a href="/files/processon-extension.zip">安装包.zip</a></li><li>2、解压到文件夹</li><li>3、打开chrome extension的开发者模式，拖动step2文件夹安装</li><li>4、安装完成后，刷新当前页面</li></ul><h3 id="平台授权"><a href="#平台授权" class="headerlink" title="平台授权"></a>平台授权</h3><p><span class="loginStatus" style="color:red">检测 Processon 登录状态</span></p><div class="table-container"><table><thead><tr><th style="text-align:left">用户昵称</th><th style="text-align:left">总文件数</th><th style="text-align:left">已用文件数</th><th style="text-align:left">可得点赞数</th><th style="text-align:left">被点赞数</th></tr></thead><tbody><tr><td style="text-align:left"><span class="fullName"></span></td><td style="text-align:left"><span class="totalcount"></span></td><td style="text-align:left"><span class="filecount"></span></td><td style="text-align:left"><span class="likecount"></span></td><td style="text-align:left"><span class="likedcount"></span></td></tr></tbody></table></div><h3 id="公开文件"><a href="#公开文件" class="headerlink" title="公开文件"></a>公开文件</h3><p><span class="robotFileCount" style="color:green"></span></p><p><del>步骤：</del></p><ul><li><del>1、打开 processon 我的文件根目录</del></li><li><del>2、创建 ProcessonRobot文件夹</del></li><li><del>3、进入ProcessonRobot文件夹</del></li><li><del>4、创建待分享的文件，建议不要超过36个</del></li></ul><p>以上步骤已改为 <b>全自动</b> 创建文件，自动创建文件数20封顶</p><p><b style="color:#cf20ef">✰✰✰✰✰✰✰✰✰需要手动将文件「发布公开」，待审核通过后可获得点赞数✰✰✰✰✰✰✰✰✰</b></p><h3 id="互助点赞"><a href="#互助点赞" class="headerlink" title="互助点赞"></a>互助点赞</h3><p><b style="color:red">完成上诉步骤，将浏览器提留在该页面，文件数会自动增加</b></p><p><b style="color:red">完成上诉步骤，将浏览器提留在该页面，文件数会自动增加</b></p><p><b style="color:red">完成上诉步骤，将浏览器提留在该页面，文件数会自动增加</b></p><script></script>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;/images/processon.png&quot; alt=&quot;processon&quot;&gt;&lt;br&gt;ProcessOn是一个在线作图工具的聚合平台，&lt;br&gt;它可以在线画流程图、思维导图、UI原型图、UML、网络拓扑图、组织结构图等等，&lt;br&gt;您无需担心下载和更新的问题，不管Mac还是Windows，一个浏览器就可以随时随地的发挥创意，规划工作。&lt;/p&gt;&lt;p&gt;不得不说ProcessOn是一个很好用的作图工具，但是免费的文件数仅9个，完全不够用，官方有两种方式对此进行升级&lt;/p&gt;&lt;ul&gt;&lt;li&gt;付费;&lt;/li&gt;&lt;li&gt;免费增加文件数量。
    
    </summary>
    
    
      <category term="奇思妙想" scheme="http://blog.develophelper.com/categories/%E5%A5%87%E6%80%9D%E5%A6%99%E6%83%B3/"/>
    
      <category term="Processon" scheme="http://blog.develophelper.com/categories/%E5%A5%87%E6%80%9D%E5%A6%99%E6%83%B3/processon/"/>
    
    
      <category term="奇思妙想" scheme="http://blog.develophelper.com/tags/%E5%A5%87%E6%80%9D%E5%A6%99%E6%83%B3/"/>
    
      <category term="Processon" scheme="http://blog.develophelper.com/tags/processon/"/>
    
  </entry>
  
  <entry>
    <title>每日算法-时间轮</title>
    <link href="http://blog.develophelper.com/article/%E6%AF%8F%E6%97%A5%E7%AE%97%E6%B3%95-%E6%97%B6%E9%97%B4%E8%BD%AE.html"/>
    <id>http://blog.develophelper.com/article/%E6%AF%8F%E6%97%A5%E7%AE%97%E6%B3%95-%E6%97%B6%E9%97%B4%E8%BD%AE.html</id>
    <published>2020-06-20T07:09:00.000Z</published>
    <updated>2022-11-28T07:51:29.847Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/images/timewheel.png" alt="timewheel"></p><p>最近做了一个订单15分钟过期变更状态机的功能，中间想过以定时任务来实现，考虑到定时任务存在一定的时间误差，于是在系统中引入了<strong>时间轮</strong>算法。</p><p>时间轮是一种环形的数据结构，类似于时钟，秒针、分针、时针分别为一层，每层分成多个格子，每个格子中存放任务集合，一个单独的线程推进时间一格一格的移动，并执行格子中的任务。它常用于延时任务，在Netty、akka、Quartz、Zookeeper等高性能组件中都存在时间轮定时器的踪影。</p><p><a href="https://github.com/SnailDev/timing_wheel" target="_blank" rel="noopener">点击查看java版时间轮实现</a></p><a id="more"></a><h3 id="时间轮相关名词解释"><a href="#时间轮相关名词解释" class="headerlink" title="时间轮相关名词解释"></a>时间轮相关名词解释</h3><ul><li>时间格：环形结构中用于存放延迟任务的区块</li><li>指针(currentTime)：指向当前操作的时间格，代表当前时间</li><li>格数(ticksPerWheel)：时间轮中时间格的个数</li><li>间隔(tickDuration)：每个时间格之间的间隔</li><li>总间隔(interval)：当前时间轮总间隔$ ticksPerWheel \times tickDuration $</li></ul><h2 id="kafka时间轮"><a href="#kafka时间轮" class="headerlink" title="kafka时间轮"></a>kafka时间轮</h2><p>在Kafka中应用了大量的延迟操作，但它并没用使用JDK自带的Timer或是DelayQueue用于延迟操作，而是使用自己开发的DelayedOperationPurgatory组件用于管理延迟操作，<br>Kafka这类分布式框架有大量延迟操作并且对性能要求及高，而java.util.Timer与java.util.concurrent.DelayQueue的插入和删除复杂度都为对数阶O(log n)并不能满足Kafka性能要求，<br>所以Kafka实现了基于时间轮的定时任务组件，该时间轮定时任务实现的插入与删除（开始定时器与暂停定时器）的时间复杂度都为常数阶O(1)。</p><h3 id="Timer"><a href="#Timer" class="headerlink" title="Timer"></a>Timer</h3><p>Timer是kafka中的定时器类，定义了共客户端调用的方法。SystemTimer是对Timer的具体实现。</p><ul><li>taskExecutor：过期任务执行线程，为了不影响性能，过期任务单独开辟线程执行；</li><li>delayQueue：一个Timer只有一个delayQueue，Timer中所有timingWheel共用，用于获取过期任务；</li><li>timingWheel：最底层时间轮tickMs(间隔)为1ms，wheelSize(格数)为20；</li></ul><p><img src="/images/kafka_timer.png" alt="kafka_timer"></p><p>关键代码：<br></p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><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">//添加任务</span></span><br><span class="line">   <span class="function"><span class="keyword">def</span> <span class="title">add</span></span>(timerTask: <span class="type">TimerTask</span>): <span class="type">Unit</span> = &#123;</span><br><span class="line">      readLock.lock()</span><br><span class="line">      <span class="keyword">try</span> &#123;</span><br><span class="line">        addTimerTaskEntry(<span class="keyword">new</span> <span class="type">TimerTaskEntry</span>(timerTask, timerTask.delayMs + <span class="type">Time</span>.<span class="type">SYSTEM</span>.hiResClockMs))</span><br><span class="line">      &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        readLock.unlock()</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="comment">//添加任务，失败直接执行</span></span><br><span class="line">   <span class="keyword">private</span> <span class="function"><span class="keyword">def</span> <span class="title">addTimerTaskEntry</span></span>(timerTaskEntry: <span class="type">TimerTaskEntry</span>): <span class="type">Unit</span> = &#123;</span><br><span class="line">      <span class="keyword">if</span> (!timingWheel.add(timerTaskEntry)) &#123;</span><br><span class="line">        <span class="keyword">if</span> (!timerTaskEntry.cancelled)</span><br><span class="line">          taskExecutor.submit(timerTaskEntry.timerTask)</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="comment">//获取过期任务，推进时间，任务执行或降轮重入</span></span><br><span class="line">   <span class="function"><span class="keyword">def</span> <span class="title">advanceClock</span></span>(timeoutMs: <span class="type">Long</span>): <span class="type">Boolean</span> = &#123;</span><br><span class="line">      <span class="keyword">var</span> bucket = delayQueue.poll(timeoutMs, <span class="type">TimeUnit</span>.<span class="type">MILLISECONDS</span>)</span><br><span class="line">      <span class="keyword">if</span> (bucket != <span class="literal">null</span>) &#123;</span><br><span class="line">        writeLock.lock()</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">          <span class="keyword">while</span> (bucket != <span class="literal">null</span>) &#123;</span><br><span class="line">            timingWheel.advanceClock(bucket.getExpiration())</span><br><span class="line">            bucket.flush(reinsert)</span><br><span class="line">            bucket = delayQueue.poll()</span><br><span class="line">          &#125;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">          writeLock.unlock()</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="literal">true</span></span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="literal">false</span></span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><p></p><h3 id="TimingWheel"><a href="#TimingWheel" class="headerlink" title="TimingWheel"></a>TimingWheel</h3><ul><li>interval：时间轮时间范围$ tickMs \times wheelSize $</li><li>buckets：TimerTaskList数组，长度对应wheelSize</li><li>currentTime：当前时间$ startMs-(startMs \% tickMs) $，取整为tickMs的倍数</li><li>overflowWheel：上级时间轮</li></ul><p><img src="/images/kafka_timingwheel.png" alt="kafka_timingwheel"></p><p>关键代码：<br></p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//添加或获取上级时间轮</span></span><br><span class="line"><span class="keyword">private</span>[<span class="keyword">this</span>] <span class="function"><span class="keyword">def</span> <span class="title">addOverflowWheel</span></span>(): <span class="type">Unit</span> = &#123;</span><br><span class="line">    synchronized &#123;</span><br><span class="line">      <span class="keyword">if</span> (overflowWheel == <span class="literal">null</span>) &#123;</span><br><span class="line">        overflowWheel = <span class="keyword">new</span> <span class="type">TimingWheel</span>(</span><br><span class="line">          tickMs = interval,</span><br><span class="line">          wheelSize = wheelSize,</span><br><span class="line">          startMs = currentTime,</span><br><span class="line">          taskCounter = taskCounter,</span><br><span class="line">          queue</span><br><span class="line">        )</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"><span class="comment">//添加任务 失败返回false直接执行</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">add</span></span>(timerTaskEntry: <span class="type">TimerTaskEntry</span>): <span class="type">Boolean</span> = &#123;</span><br><span class="line">    <span class="keyword">val</span> expiration = timerTaskEntry.expirationMs</span><br><span class="line">    <span class="keyword">if</span> (timerTaskEntry.cancelled) &#123;</span><br><span class="line">      <span class="comment">//取消</span></span><br><span class="line">      <span class="literal">false</span></span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (expiration &lt; currentTime + tickMs) &#123;</span><br><span class="line">      <span class="comment">//已过期</span></span><br><span class="line">      <span class="literal">false</span></span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (expiration &lt; currentTime + interval) &#123;</span><br><span class="line">      <span class="comment">//当前时间轮可以容纳该任务</span></span><br><span class="line">      <span class="keyword">val</span> virtualId = expiration / tickMs</span><br><span class="line">      <span class="keyword">val</span> bucket = buckets((virtualId % wheelSize.toLong).toInt)</span><br><span class="line">      bucket.add(timerTaskEntry)</span><br><span class="line">      <span class="keyword">if</span> (bucket.setExpiration(virtualId * tickMs)) &#123;</span><br><span class="line">        queue.offer(bucket)</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="literal">true</span></span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="comment">//加入上级时间轮</span></span><br><span class="line">      <span class="keyword">if</span> (overflowWheel == <span class="literal">null</span>) addOverflowWheel()</span><br><span class="line">      overflowWheel.add(timerTaskEntry)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"><span class="comment">//推进时间</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">advanceClock</span></span>(timeMs: <span class="type">Long</span>): <span class="type">Unit</span> = &#123;</span><br><span class="line">    <span class="keyword">if</span> (timeMs &gt;= currentTime + tickMs) &#123;</span><br><span class="line">      currentTime = timeMs - (timeMs % tickMs)</span><br><span class="line">      <span class="comment">//推进上级时间轮时间</span></span><br><span class="line">      <span class="keyword">if</span> (overflowWheel != <span class="literal">null</span>) overflowWheel.advanceClock(currentTime)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><p></p><h3 id="TimerTaskList"><a href="#TimerTaskList" class="headerlink" title="TimerTaskList"></a>TimerTaskList</h3><ul><li>TimerTaskEntry：用于封装TimerTask</li></ul><p><img src="/images/kafka_timertasklist.png" alt="kafka_timertasklist"></p><h3 id="TimerTask"><a href="#TimerTask" class="headerlink" title="TimerTask"></a>TimerTask</h3><p>继承java Runnable接口</p><p><img src="/images/kafka_timertask.png" alt="kafka_timertask"></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;/images/timewheel.png&quot; alt=&quot;timewheel&quot;&gt;&lt;/p&gt;&lt;p&gt;最近做了一个订单15分钟过期变更状态机的功能，中间想过以定时任务来实现，考虑到定时任务存在一定的时间误差，于是在系统中引入了&lt;strong&gt;时间轮&lt;/strong&gt;算法。&lt;/p&gt;&lt;p&gt;时间轮是一种环形的数据结构，类似于时钟，秒针、分针、时针分别为一层，每层分成多个格子，每个格子中存放任务集合，一个单独的线程推进时间一格一格的移动，并执行格子中的任务。它常用于延时任务，在Netty、akka、Quartz、Zookeeper等高性能组件中都存在时间轮定时器的踪影。&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/SnailDev/timing_wheel&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;点击查看java版时间轮实现&lt;/a&gt;&lt;/p&gt;
    
    </summary>
    
    
      <category term="每日算法" scheme="http://blog.develophelper.com/categories/%E6%AF%8F%E6%97%A5%E7%AE%97%E6%B3%95/"/>
    
    
      <category term="每日算法" scheme="http://blog.develophelper.com/tags/%E6%AF%8F%E6%97%A5%E7%AE%97%E6%B3%95/"/>
    
  </entry>
  
  <entry>
    <title>每日算法-布隆过滤器</title>
    <link href="http://blog.develophelper.com/article/%E6%AF%8F%E6%97%A5%E7%AE%97%E6%B3%95-%E5%B8%83%E9%9A%86%E8%BF%87%E6%BB%A4%E5%99%A8.html"/>
    <id>http://blog.develophelper.com/article/%E6%AF%8F%E6%97%A5%E7%AE%97%E6%B3%95-%E5%B8%83%E9%9A%86%E8%BF%87%E6%BB%A4%E5%99%A8.html</id>
    <published>2020-06-17T06:24:00.000Z</published>
    <updated>2021-01-21T07:48:17.130Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/images/BloomFilter.png" alt="BloomFilter"></p><p>最近身边的朋友讨论「布隆过滤器」的频次有点多，刚好自己也不太了解，于是乎研究一下。学习一个知识，首先了解该知识能带来的价值是什么，那么撇开算法本身，先来了解下这个东西的实际应用场景，大概的场景有以下几种：</p><ul><li>解决缓存穿透问题</li><li>网络爬虫对URL去重</li><li>垃圾邮件过滤</li><li>秒杀系统</li></ul><h3 id="什么是布隆过滤器"><a href="#什么是布隆过滤器" class="headerlink" title="什么是布隆过滤器"></a>什么是布隆过滤器</h3><p>本质上布隆过滤器是一种数据结构，比较巧妙的概率型数据结构，它的特点是高效地插入和查询，而根据查询结果可以判断某个对象 <strong>一定不存在或者可能存在</strong>。</p><p>相比于传统的List、Set、Map 等数据结构，它更高效、占用空间更少，但是缺点是得到结果是概率性的，而不是确切的，同时布隆过滤器还有一个缺陷就是 <strong>数据只能插入不能删除</strong>。</p><a id="more"></a><h3 id="数据如何存入布隆过滤器"><a href="#数据如何存入布隆过滤器" class="headerlink" title="数据如何存入布隆过滤器"></a>数据如何存入布隆过滤器</h3><p>布隆过滤器是由一个很长的<code>bit数组</code>和一系列<code>哈希函数</code>组成的。数组的每个元素都只占1bit空间，并且每个元素只能为0或1。布隆过滤器还拥有k个哈希函数，当一个元素加入布隆过滤器时，会使用k个哈希函数对其进行k次计算,得到k个哈希值，并且根据得到的哈希值，在bit数组中把对应下标的值置位1。</p><p>判断某个数是否在布隆过滤器中，就对该元素进行k次哈希计算，得到的值在bit数组中判断对应位置的所有元素是否都为1，如素都为1，就说明这个值在布隆过滤器中。</p><h3 id="为什么会有误判"><a href="#为什么会有误判" class="headerlink" title="为什么会有误判"></a>为什么会有误判</h3><p>当插入的元素越来越多时，当一个不在布隆过滤器中的元素，经过同样规则的哈希计算之后，得到的值在bit数组中查询，有可能这些位置因为其他的元素提前被置1了。因此布隆过滤器存在误判的情况，但是如果布隆过滤器判断某个元素不在布隆过滤器中，那么这个值就一定不在。</p><h3 id="应用场景举例"><a href="#应用场景举例" class="headerlink" title="应用场景举例"></a>应用场景举例</h3><p><code>背景</code> 现在有个100亿个黑名单网页数据，每个网页的URL占用64字节。现在想要实现一种网页过滤系统，可以根据网页的URL判断该网站是否在黑名单上，请设计该系统。</p><p><code>需求</code> 可以允许有0.01%以下的判断失误率，并且使用的总空间不要超过200G。</p><p>分析一下，这里一共有4个常量：</p><p><code>100亿条黑名单数据</code>，<code>每条数据占64个字节</code>，<code>万分之一的失误率</code>，<code>总空间不要超过200G</code>。</p><ul><li><p>如果不考虑布隆过滤器，那么这里存储100亿条数据就需要 $ 100亿 \times 64字节 = 596G $ 显然超过300G。</p></li><li><p>如果使用布隆过滤器，在满足有<strong>100亿条数据</strong> 并且允许<strong>万分之一的失误率</strong>的布隆过滤器需要多大的bit数组呢？</p></li></ul><p>下面来分析下使用布隆过滤器的情况（设bit数组的大小为m，样本数量为n，失误率为p）：</p><p>由题可知 $ n=100亿，n=0.01% $，根据布隆过滤器大小m的公式：</p><script type="math/tex;mode=display">m=-\frac{n\ln p}{(\ln 2)^2}</script><p>求得 m = 19.19n，向上取整为 20n，2000亿字节，约为186G，满足条件。</p><p>算完m，我们顺便来算下m，n已知，这时满足最小误差的k是几个。根据哈希函数个数k的公式：</p><script type="math/tex;mode=display">k=\ln 2 \times \frac{m}{n}</script><p>求得 k = 14，即需要14个哈希函数。</p><p>通过通过 m = 20n， k = 14我们再来算下真实的失误率。根据布隆过滤器真实失误率p公式：</p><script type="math/tex;mode=display">p=(1-e^{-\frac{nk}{m}})^k</script><p>求得 p = 0.006%，即布隆过滤器的真实失误率为0.006%。</p><p>通过布隆过滤器公式也可以看出：</p><blockquote><p>单个数据的大小不影响布隆过滤器大小，因为样本会通过哈希函数得到输出值</p></blockquote><p>就好比上面的<code>每个网页的URL占用64字节</code>这个数据大小跟布隆过滤器大小没啥关系。</p><p>这三个公式就是有关布隆过滤器已经推倒出的公式，下面我们来看看这个公式是如何推导出来的。</p><h3 id="布隆过滤器公式推导"><a href="#布隆过滤器公式推导" class="headerlink" title="布隆过滤器公式推导"></a>布隆过滤器公式推导</h3><p>讲公式，应该先知道几个关键的常量：<br><code>误判率p</code>、<code>布隆过滤器长度m</code>、<code>元素个数n</code>、<code>哈希函数个数k</code></p><p><code>前提条件</code>：假设每个元素哈希得到的值分布到m数组上的每一个数组节点的概率是相等的。</p><ol><li><p>假设布隆过滤器长度为m，元素个数n为1，哈希函数个数k也为1。那么在插入时某一数组节点没有被置为1的概率。</p><script type="math/tex;mode=display">1-\frac{1}{m}</script></li><li><p>如果上面其它不变，而哈希函数个数变成k个，那么在插入时某一数组节点没有被置为1的概率。</p><script type="math/tex;mode=display">{(1-\frac{1}{m})}^k</script></li><li><p>如果元素个数变成n个，而哈希函数个数变成k个，那么在插入时某一数组节点没有被置为1的概率。</p><script type="math/tex;mode=display">{(1-\frac{1}{m})}^{kn}</script></li><li><p>从上面推导出的是： 当布隆过滤器长度为m，元素个数变成n个，哈希函数个数变成k个的时候，某一节点被置为1的概率为</p><script type="math/tex;mode=display">1-{(1-\frac{1}{m})}^{kn}</script></li><li><p>这个还需要考虑到，一个元素通过hash会生成多个k，放入m数组中，所以需要这k个值都为1才会认为该该元素已经存在，转换如下。</p><script type="math/tex;mode=display">(1-{(1-\frac{1}{m})}^{kn})^k</script><p>=&gt;</p><script type="math/tex;mode=display">[1-{(1-\frac{1}{m}}^{nk})]^k  \approx  (1-e^{-\frac{nk}{m}})^k</script></li></ol><p><code>思考</code> 为什么上面这个公式的值就是最终的误差率?</p><p>因为当一个布隆过滤器中不存在的元素进来的是的时候，首先通过hash算法产生k个哈希值，分布在m数组上都为1的的概率不就是上面推导出的这个公式吗，那不就是误差吗?明明是不存在的值，却有这个概率表明已经存在。</p><p><code>思考</code> 给定的m和n,思考k值为多少误差会最小。</p><p>为什么k值的大小不合理会影响误差呢？</p><p>我们来思考下，一个元素最终生成k个hash值，那么会在数组m上的k个位置标记为1。</p><p>假设k为1，那么每次进来只在m上的某一个位置标记为1，这样的话如果一个新元素进来刚好hash值也在这里，而不用其它位置来判断是否为1，这个误差就会比较大。</p><p>假设k为m，那么第一个元素进来，在m上所有位置上都表为1了 ，以后只要进来一个元素就会标记为已存在。这个误差也太大了。</p><p>上面只是举了两个极端的例子，但也说明k值太大、太小都不好，它的最优值一定跟m、n存在某种关系。</p><p>它们之间的关系只要记住下面这个公式就可以了。</p><script type="math/tex;mode=display">k=\ln 2 \times \frac{m}{n}</script><h3 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h3><p><a href="https://zhuanlan.zhihu.com/p/43263751" target="_blank" rel="external nofollow noopener noreferrer">详解布隆过滤器的原理，使用场景和注意事项</a></p><p><a href="https://blog.csdn.net/gaoyueace/article/details/90410735" target="_blank" rel="external nofollow noopener noreferrer">布隆过滤器概念及其公式推导</a></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;/images/BloomFilter.png&quot; alt=&quot;BloomFilter&quot;&gt;&lt;/p&gt;&lt;p&gt;最近身边的朋友讨论「布隆过滤器」的频次有点多，刚好自己也不太了解，于是乎研究一下。学习一个知识，首先了解该知识能带来的价值是什么，那么撇开算法本身，先来了解下这个东西的实际应用场景，大概的场景有以下几种：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;解决缓存穿透问题&lt;/li&gt;&lt;li&gt;网络爬虫对URL去重&lt;/li&gt;&lt;li&gt;垃圾邮件过滤&lt;/li&gt;&lt;li&gt;秒杀系统&lt;/li&gt;&lt;/ul&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;strong&gt;一定不存在或者可能存在&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;相比于传统的List、Set、Map 等数据结构，它更高效、占用空间更少，但是缺点是得到结果是概率性的，而不是确切的，同时布隆过滤器还有一个缺陷就是 &lt;strong&gt;数据只能插入不能删除&lt;/strong&gt;。&lt;/p&gt;
    
    </summary>
    
    
      <category term="每日算法" scheme="http://blog.develophelper.com/categories/%E6%AF%8F%E6%97%A5%E7%AE%97%E6%B3%95/"/>
    
    
      <category term="每日算法" scheme="http://blog.develophelper.com/tags/%E6%AF%8F%E6%97%A5%E7%AE%97%E6%B3%95/"/>
    
  </entry>
  
  <entry>
    <title>每日算法-约瑟夫环问题</title>
    <link href="http://blog.develophelper.com/article/leetcode-joseph-ring.html"/>
    <id>http://blog.develophelper.com/article/leetcode-joseph-ring.html</id>
    <published>2020-04-02T14:45:00.000Z</published>
    <updated>2021-01-21T07:48:17.129Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/images/joseph_ring_1.png" alt="josephring"></p><p>最近开始在做算法题，不全是为了找工作，为了面试，旨在不断思考，锻炼思维，同时查阅别人的解题方法，开拓自己的思路。在遇到实际问题时，可以有料参考，有迹可循。</p><h3 id="约瑟夫问题"><a href="#约瑟夫问题" class="headerlink" title="约瑟夫问题"></a>约瑟夫问题</h3><blockquote><p>这个问题是以弗拉维奥·约瑟夫命名的，他是1世纪的一名犹太历史学家。他在自己的日记中写道，他和他的40个战友被罗马军队包围在洞中。<br>他们讨论是自杀还是被俘，最终决定自杀，并以抽签的方式决定谁杀掉谁。约瑟夫斯和另外一个人是最后两个留下的人。约瑟夫斯说服了那个人，他们将向罗马军队投降，不再自杀。<br>约瑟夫斯把他的存活归因于运气或天意，他不知道是哪一个。 —— 【约瑟夫问题】维基百科</p></blockquote><p>解题秘诀：<strong>只关心最终活着那个人的序号变化</strong></p><a id="more"></a><h3 id="举例说明"><a href="#举例说明" class="headerlink" title="举例说明"></a>举例说明</h3><p>我们也给每个人一个编号（索引值），单个人用字母代替，下面这个例子是N=8 m=3的例子</p><p>定义F(n,m)表示最后剩下那个人的索引号，因此只关心最后剩下来这个人的索引号的变化情况即可</p><p><img src="/images/joseph_ring_1.png" alt="josephring"></p><p>从8个人开始，每次杀掉一个人，去掉被杀的人，然后把杀掉那个人之后的第一个人作为开头重新编号</p><ul><li>第一次C被杀掉，人数变成7，D作为开头，（最终活下来的G的编号从6变成3）</li><li>第二次F被杀掉，人数变成6，G作为开头，（最终活下来的G的编号从3变成0）</li><li>第三次A被杀掉，人数变成5，B作为开头，（最终活下来的G的编号从0变成3）</li><li>以此类推，当只剩一个人时，他的编号必定为0！（重点！）</li></ul><h3 id="反推过程"><a href="#反推过程" class="headerlink" title="反推过程"></a>反推过程</h3><p>从N = 7 到N = 8 的过程</p><p>如何才能将N = 7 的排列变回到N = 8 呢？</p><p>我们先把被杀掉的C补充回来，然后右移m个人，发现溢出了，再把溢出的补充在最前面</p><p>神奇了 经过这个操作就恢复了N = 8 的排列了！</p><p><img src="/images/joseph_ring_2.png" alt="josephring"></p><p>因此我们可以推出递推公式 $ f(8,3) = [f(7, 3) + 3] \% 8 $</p><p>进行推广泛化，即 $ f(n,m) = [f(n-1, m) + m] \% n $</p><h3 id="公式导出"><a href="#公式导出" class="headerlink" title="公式导出"></a>公式导出</h3><p>再把n=1这个最初的情况加上，就得到递推公式</p><script type="math/tex;mode=display">f(n，m)=\begin{cases}0,&\text{n=1}\\ [f(n-1, m)+m]\%n,&\text{n>1}\end{cases}</script><h3 id="写出代码"><a href="#写出代码" class="headerlink" title="写出代码"></a>写出代码</h3><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"><span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">lastRemaining</span><span class="params">(<span class="keyword">int</span> n, <span class="keyword">int</span> m)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">int</span> pos = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">2</span>; i&lt;=n; i++)&#123;</span><br><span class="line">          pos = (pos + m) % i;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> pos;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;/images/joseph_ring_1.png&quot; alt=&quot;josephring&quot;&gt;&lt;/p&gt;&lt;p&gt;最近开始在做算法题，不全是为了找工作，为了面试，旨在不断思考，锻炼思维，同时查阅别人的解题方法，开拓自己的思路。在遇到实际问题时，可以有料参考，有迹可循。&lt;/p&gt;&lt;h3 id=&quot;约瑟夫问题&quot;&gt;&lt;a href=&quot;#约瑟夫问题&quot; class=&quot;headerlink&quot; title=&quot;约瑟夫问题&quot;&gt;&lt;/a&gt;约瑟夫问题&lt;/h3&gt;&lt;blockquote&gt;&lt;p&gt;这个问题是以弗拉维奥·约瑟夫命名的，他是1世纪的一名犹太历史学家。他在自己的日记中写道，他和他的40个战友被罗马军队包围在洞中。&lt;br&gt;他们讨论是自杀还是被俘，最终决定自杀，并以抽签的方式决定谁杀掉谁。约瑟夫斯和另外一个人是最后两个留下的人。约瑟夫斯说服了那个人，他们将向罗马军队投降，不再自杀。&lt;br&gt;约瑟夫斯把他的存活归因于运气或天意，他不知道是哪一个。 —— 【约瑟夫问题】维基百科&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;解题秘诀：&lt;strong&gt;只关心最终活着那个人的序号变化&lt;/strong&gt;&lt;/p&gt;
    
    </summary>
    
    
      <category term="每日算法" scheme="http://blog.develophelper.com/categories/%E6%AF%8F%E6%97%A5%E7%AE%97%E6%B3%95/"/>
    
      <category term="leetcode" scheme="http://blog.develophelper.com/categories/%E6%AF%8F%E6%97%A5%E7%AE%97%E6%B3%95/leetcode/"/>
    
    
      <category term="每日算法" scheme="http://blog.develophelper.com/tags/%E6%AF%8F%E6%97%A5%E7%AE%97%E6%B3%95/"/>
    
      <category term="leetcode" scheme="http://blog.develophelper.com/tags/leetcode/"/>
    
  </entry>
  
  <entry>
    <title>热门平台短视频地址解析水印去除</title>
    <link href="http://blog.develophelper.com/article/short-video-parse.html"/>
    <id>http://blog.develophelper.com/article/short-video-parse.html</id>
    <published>2020-02-22T10:25:46.000Z</published>
    <updated>2021-01-21T07:48:17.130Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/images/douyinvskuaishou.jpeg" alt="douyin-vs-kuaishou"></p><h2 id="解析模块"><a href="#解析模块" class="headerlink" title="解析模块"></a>解析模块</h2><div class="post black-box archive-page">使用方法：<ol><li>1、打开短视频APP，点开某个视频，点击右下角分享按钮，在分享弹框中点击复制链接或通过分享到微信QQ等获取分享链接</li><li>2、将刚才复制的链接粘贴到下面的输入框，然后按Enter键，例如：https://v.douyin.com/GBGy9c/</li></ol><div class="m_search" style="color:#fff;background:#000;width:auto"><form name="searchform" id="parseform" action class="form u-search-form"><input type="text" class="input u-search-input" placeholder="请输入短视频分享链接"> <i class="fa fa-search"></i></form><script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script><script>$("#parseform").on("submit",(function(t){var o=$(".u-search-input").val();if((o=/(http:\/\/|https:\/\/)((\w|=|\?|\.|\/|&|-)+)/g.exec(o)[0].trim()).indexOf("http")<0)return $(".msg").text("请输入分享链接（网址）"),!1;var e="";return o.indexOf("douyin")>-1&&(e="douyin"),$.ajax({type:"POST",url:EXTERNAL_API+"short-video/parse",contentType:"application/json",data:JSON.stringify({videoPlatform:e,videoShareUrl:o}),dataType:"json",success:function(t){console.log(t),0==t.code?($(".download").text(t.data.originTitle),$(".download").attr("href",t.data.originUrl),$(".msg").text(""),$(".download").attr("hidden",!1)):($(".download").attr("hidden",!0),$(".msg").text(t.msg))},error:function(t,o,e){}}),!1}))</script></div><div><a class="download" hidden target="_blank">下载地址</a> <span class="msg" style="color:red"></span></div></div><a id="more"></a><h2 id="常见疑问"><a href="#常见疑问" class="headerlink" title="常见疑问"></a>常见疑问</h2><ol><li>Android手机上可以用吗？</li></ol><p>可以的，Android手机在常用的Chrome、UC、360、QQ等浏览器上都可以很方便的使用本站。推荐使用Chrome浏览器获得最佳下载体验。</p><ol><li>iOS设备(iPhone、iPad)上点击下载视频按钮后，跳转到视频页面，并没有直接下载，如何处理？</li></ol><p>因Safari及微信内置浏览器均不支持下载文件，ios用户建议使用小程序”今天去水印”。也可以在App Store下载免费的Documents 6，在Documents的内置浏览器中使用本站，Documents支持将下载的视频移到手机相册。</p><ol><li>电脑上部分浏览器不支持直接下载，如何处理？</li></ol><p>电脑上部分浏览器不支持直接下载，请点击”复制视频链接”按钮，在浏览器中打开复制的链接，在视频画面上点击右键，选择”视频另存为”或”存储为”来下载视频。推荐使用谷歌Chrome浏览器、360浏览器、QQ浏览器等主流浏览器。</p><ol><li>有没有其他更方面的方式可以快速下载视频保存到手机呢？</li></ol><p>暂无。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;/images/douyinvskuaishou.jpeg&quot; alt=&quot;douyin-vs-kuaishou&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;解析模块&quot;&gt;&lt;a href=&quot;#解析模块&quot; class=&quot;headerlink&quot; title=&quot;解析模块&quot;&gt;&lt;/a&gt;解析模块&lt;/h2&gt;&lt;div class=&quot;post black-box archive-page&quot;&gt;使用方法：&lt;ol&gt;&lt;li&gt;1、打开短视频APP，点开某个视频，点击右下角分享按钮，在分享弹框中点击复制链接或通过分享到微信QQ等获取分享链接&lt;/li&gt;&lt;li&gt;2、将刚才复制的链接粘贴到下面的输入框，然后按Enter键，例如：https://v.douyin.com/GBGy9c/&lt;/li&gt;&lt;/ol&gt;&lt;div class=&quot;m_search&quot; style=&quot;color:#fff;background:#000;width:auto&quot;&gt;&lt;form name=&quot;searchform&quot; id=&quot;parseform&quot; action class=&quot;form u-search-form&quot;&gt;&lt;input type=&quot;text&quot; class=&quot;input u-search-input&quot; placeholder=&quot;请输入短视频分享链接&quot;&gt; &lt;i class=&quot;fa fa-search&quot;&gt;&lt;/i&gt;&lt;/form&gt;&lt;script src=&quot;https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js&quot;&gt;&lt;/script&gt;&lt;script&gt;$(&quot;#parseform&quot;).on(&quot;submit&quot;,(function(t){var o=$(&quot;.u-search-input&quot;).val();if((o=/(http:\/\/|https:\/\/)((\w|=|\?|\.|\/|&amp;|-)+)/g.exec(o)[0].trim()).indexOf(&quot;http&quot;)&lt;0)return $(&quot;.msg&quot;).text(&quot;请输入分享链接（网址）&quot;),!1;var e=&quot;&quot;;return o.indexOf(&quot;douyin&quot;)&gt;-1&amp;&amp;(e=&quot;douyin&quot;),$.ajax({type:&quot;POST&quot;,url:EXTERNAL_API+&quot;short-video/parse&quot;,contentType:&quot;application/json&quot;,data:JSON.stringify({videoPlatform:e,videoShareUrl:o}),dataType:&quot;json&quot;,success:function(t){console.log(t),0==t.code?($(&quot;.download&quot;).text(t.data.originTitle),$(&quot;.download&quot;).attr(&quot;href&quot;,t.data.originUrl),$(&quot;.msg&quot;).text(&quot;&quot;),$(&quot;.download&quot;).attr(&quot;hidden&quot;,!1)):($(&quot;.download&quot;).attr(&quot;hidden&quot;,!0),$(&quot;.msg&quot;).text(t.msg))},error:function(t,o,e){}}),!1}))&lt;/script&gt;&lt;/div&gt;&lt;div&gt;&lt;a class=&quot;download&quot; hidden target=&quot;_blank&quot;&gt;下载地址&lt;/a&gt; &lt;span class=&quot;msg&quot; style=&quot;color:red&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;
    
    </summary>
    
    
      <category term="短视频" scheme="http://blog.develophelper.com/categories/%E7%9F%AD%E8%A7%86%E9%A2%91/"/>
    
      <category term="去水印" scheme="http://blog.develophelper.com/categories/%E7%9F%AD%E8%A7%86%E9%A2%91/%E5%8E%BB%E6%B0%B4%E5%8D%B0/"/>
    
    
      <category term="短视频" scheme="http://blog.develophelper.com/tags/%E7%9F%AD%E8%A7%86%E9%A2%91/"/>
    
      <category term="去水印" scheme="http://blog.develophelper.com/tags/%E5%8E%BB%E6%B0%B4%E5%8D%B0/"/>
    
  </entry>
  
  <entry>
    <title>git使用之批量操作shell脚本</title>
    <link href="http://blog.develophelper.com/article/smnpo-git-use-operate-batch.html"/>
    <id>http://blog.develophelper.com/article/smnpo-git-use-operate-batch.html</id>
    <published>2019-05-06T15:13:32.000Z</published>
    <updated>2020-02-15T15:23:10.033Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/images/git.jpg" alt="git platform"></p><p>之前介绍了<a href="/article/smnpo-git-use-github-gitlab.html">git多账号ssh-key管理</a> 中涉及到 user.name 和 user.email 按照项目来管理<br></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ git config --<span class="built_in">local</span> user.name <span class="string">"你的名字"</span></span><br><span class="line">$ git config --<span class="built_in">local</span> user.email  <span class="string">"你的邮箱"</span></span><br></pre></td></tr></table></figure><br>这样项目一多，设置就成了噩梦，本篇提供批量设置的脚本<br><a id="more"></a><p></p><h2 id="一、批量按项目设置git用户信息"><a href="#一、批量按项目设置git用户信息" class="headerlink" title="一、批量按项目设置git用户信息"></a>一、批量按项目设置git用户信息</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/sh</span></span><br><span class="line"><span class="comment">##This bash file can update all git project in given directory</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"Please input your name: "</span></span><br><span class="line"><span class="built_in">read</span> name</span><br><span class="line"><span class="built_in">echo</span> <span class="string">"You had input name: <span class="variable">$name</span>"</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"Please input your email: "</span></span><br><span class="line"><span class="built_in">read</span> email</span><br><span class="line"><span class="built_in">echo</span> <span class="string">"You had input email: <span class="variable">$email</span>"</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#in case this file being invoked form other path</span></span><br><span class="line">base_dir=$(dirname <span class="string">"<span class="variable">$0</span>"</span>)</span><br><span class="line"><span class="comment">#a tool script for colorful output</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">cd</span> <span class="variable">$base_dir</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#read given directory as repositories directory,</span></span><br><span class="line"><span class="comment">#or current directory</span></span><br><span class="line">project_dir=<span class="string">"."</span></span><br><span class="line"><span class="keyword">if</span> [ ! -z <span class="variable">$1</span> ]</span><br><span class="line"><span class="keyword">then</span></span><br><span class="line">  project_dir=<span class="variable">$1</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> [ ! -d <span class="variable">$project_dir</span> ]</span><br><span class="line"><span class="keyword">then</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">"<span class="variable">$project_dir</span> is not a directory."</span></span><br><span class="line">  <span class="built_in">exit</span> 1</span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"Set repository base directory to <span class="variable">$project_dir</span>"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> project <span class="keyword">in</span> `ls <span class="variable">$project_dir</span>`</span><br><span class="line"><span class="keyword">do</span></span><br><span class="line">  <span class="keyword">if</span> [ -d <span class="variable">$project</span> ]</span><br><span class="line">  <span class="keyword">then</span></span><br><span class="line">    <span class="built_in">cd</span> <span class="variable">$project</span></span><br><span class="line">    <span class="comment">#update git repository</span></span><br><span class="line">    <span class="keyword">if</span> [ -d <span class="string">'.git'</span> ] </span><br><span class="line">    <span class="keyword">then</span></span><br><span class="line">      <span class="built_in">echo</span> <span class="string">"Setting project:<span class="variable">$project</span>"</span></span><br><span class="line">      git config --<span class="built_in">local</span> user.name <span class="string">"<span class="variable">$name</span>"</span></span><br><span class="line">      git config --<span class="built_in">local</span> user.email <span class="string">"<span class="variable">$email</span>"</span></span><br><span class="line">      <span class="built_in">echo</span> <span class="string">"Done."</span></span><br><span class="line">      <span class="built_in">echo</span></span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">      <span class="built_in">echo</span> <span class="string">"<span class="variable">$project</span> is not a repository.\n"</span></span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line">    <span class="built_in">cd</span> ..</span><br><span class="line">  <span class="keyword">fi</span></span><br><span class="line"><span class="keyword">done</span></span><br><span class="line"><span class="built_in">exit</span> 0</span><br></pre></td></tr></table></figure><h2 id="二、批量-pull-项目"><a href="#二、批量-pull-项目" class="headerlink" title="二、批量 pull 项目"></a>二、批量 pull 项目</h2><p>同理可以写出如下脚本：<br></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/sh</span></span><br><span class="line"><span class="comment">##This bash file can update all git project in given directory</span></span><br><span class="line"><span class="comment">#in case this file being invoked form other path</span></span><br><span class="line">base_dir=$(dirname <span class="string">"<span class="variable">$0</span>"</span>)</span><br><span class="line"><span class="comment">#a tool script for colorful output</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">cd</span> <span class="variable">$base_dir</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#read given directory as repositories directory,</span></span><br><span class="line"><span class="comment">#or current directory</span></span><br><span class="line">project_dir=<span class="string">"."</span></span><br><span class="line"><span class="keyword">if</span> [ ! -z <span class="variable">$1</span> ]</span><br><span class="line"><span class="keyword">then</span></span><br><span class="line">  project_dir=<span class="variable">$1</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> [ ! -d <span class="variable">$project_dir</span> ]</span><br><span class="line"><span class="keyword">then</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">"<span class="variable">$project_dir</span> is not a directory."</span></span><br><span class="line">  <span class="built_in">exit</span> 1</span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"Set repository base directory to <span class="variable">$project_dir</span>"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> project <span class="keyword">in</span> `ls <span class="variable">$project_dir</span>`</span><br><span class="line"><span class="keyword">do</span></span><br><span class="line">  <span class="keyword">if</span> [ -d <span class="variable">$project</span> ]</span><br><span class="line">  <span class="keyword">then</span></span><br><span class="line">    <span class="built_in">cd</span> <span class="variable">$project</span></span><br><span class="line">    <span class="comment">#update git repository</span></span><br><span class="line">    <span class="keyword">if</span> [ -d <span class="string">'.git'</span> ] </span><br><span class="line">    <span class="keyword">then</span></span><br><span class="line">      <span class="built_in">echo</span> <span class="string">"Updating project:<span class="variable">$project</span>"</span></span><br><span class="line">      git pull --all</span><br><span class="line">      <span class="built_in">echo</span> <span class="string">"Done."</span></span><br><span class="line">      <span class="built_in">echo</span></span><br><span class="line">    <span class="comment">#update svn repository optional</span></span><br><span class="line">    <span class="keyword">elif</span> [ -d <span class="string">'.svn'</span> ]</span><br><span class="line">    <span class="keyword">then</span></span><br><span class="line">      <span class="built_in">echo</span> <span class="string">"Updating project:<span class="variable">$project</span>"</span></span><br><span class="line">      svn update</span><br><span class="line">      <span class="built_in">echo</span> <span class="string">"Done."</span></span><br><span class="line">      <span class="built_in">echo</span></span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">      <span class="built_in">echo</span> <span class="string">"<span class="variable">$project</span> is not a repository.\n"</span></span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line">    <span class="built_in">cd</span> ..</span><br><span class="line">  <span class="keyword">fi</span></span><br><span class="line"><span class="keyword">done</span></span><br><span class="line"><span class="built_in">exit</span> 0</span><br></pre></td></tr></table></figure><p></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;/images/git.jpg&quot; alt=&quot;git platform&quot;&gt;&lt;/p&gt;&lt;p&gt;之前介绍了&lt;a href=&quot;/article/smnpo-git-use-github-gitlab.html&quot;&gt;git多账号ssh-key管理&lt;/a&gt; 中涉及到 user.name 和 user.email 按照项目来管理&lt;br&gt;&lt;/p&gt;&lt;figure class=&quot;highlight bash&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;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;$ git config --&lt;span class=&quot;built_in&quot;&gt;local&lt;/span&gt; user.name &lt;span class=&quot;string&quot;&gt;&quot;你的名字&quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;$ git config --&lt;span class=&quot;built_in&quot;&gt;local&lt;/span&gt; user.email  &lt;span class=&quot;string&quot;&gt;&quot;你的邮箱&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;br&gt;这样项目一多，设置就成了噩梦，本篇提供批量设置的脚本&lt;br&gt;
    
    </summary>
    
    
      <category term="smnpo" scheme="http://blog.develophelper.com/categories/smnpo/"/>
    
      <category term="git" scheme="http://blog.develophelper.com/categories/smnpo/git/"/>
    
      <category term="shell" scheme="http://blog.develophelper.com/categories/smnpo/git/shell/"/>
    
    
      <category term="smnpo" scheme="http://blog.develophelper.com/tags/smnpo/"/>
    
      <category term="git" scheme="http://blog.develophelper.com/tags/git/"/>
    
      <category term="shell" scheme="http://blog.develophelper.com/tags/shell/"/>
    
  </entry>
  
  <entry>
    <title>git使用之多账号ssh-key管理</title>
    <link href="http://blog.develophelper.com/article/smnpo-git-use-github-gitlab.html"/>
    <id>http://blog.develophelper.com/article/smnpo-git-use-github-gitlab.html</id>
    <published>2019-05-05T13:13:32.000Z</published>
    <updated>2020-02-15T15:23:10.032Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/images/GitLab-vs-GitHub-vs-bitbucket-1.jpg" alt="git platform"></p><p>日常使用 git 作为仓库使用时，会遇到以下情况：</p><ul><li>有两个 github 账号（至少两个），一台电脑同时连接这两个账号进行维护</li><li>私人 github 账号，公司 gitlab 账号</li></ul><a id="more"></a><h2 id="一、清除-git-的全局设置（针对已安装-git）"><a href="#一、清除-git-的全局设置（针对已安装-git）" class="headerlink" title="一、清除 git 的全局设置（针对已安装 git）"></a>一、清除 git 的全局设置（针对已安装 git）</h2><p>新安装 git 跳过。</p><p>若之前对 git 设置过全局的 user.name 和 user.email。<br>类似(用git config —global —list 进行查看你是否设置)</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ git config --global user.name <span class="string">"你的名字"</span></span><br><span class="line">$ git config --global user.email  <span class="string">"你的邮箱"</span></span><br></pre></td></tr></table></figure><p>必须删除该设置<br></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ git config --global --<span class="built_in">unset</span> user.name <span class="string">"你的名字"</span></span><br><span class="line">$ git config --global --<span class="built_in">unset</span> user.email <span class="string">"你的邮箱"</span></span><br></pre></td></tr></table></figure><br>按项目设置<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ git config --<span class="built_in">local</span> user.name <span class="string">"你的名字"</span></span><br><span class="line">$ git config --<span class="built_in">local</span> user.email  <span class="string">"你的邮箱"</span></span><br></pre></td></tr></table></figure><p></p><h2 id="二、生成新的-SSH-keys"><a href="#二、生成新的-SSH-keys" class="headerlink" title="二、生成新的 SSH keys"></a>二、生成新的 SSH keys</h2><p>（1）#GitHub的钥匙<br></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">ssh-keygen -t rsa -C <span class="string">"xxx@xx.com"</span></span><br><span class="line">Enter file <span class="keyword">in</span> <span class="built_in">which</span> to save the key (/Users/kingboy/.ssh/id_rsa): /Users/kingboy/.ssh/github_id_rsa</span><br></pre></td></tr></table></figure><br>（2）#gitlab<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">ssh-keygen -t rsa -C <span class="string">"xxx@xx.com"</span></span><br><span class="line">Enter file <span class="keyword">in</span> <span class="built_in">which</span> to save the key (/Users/kingboy/.ssh/id_rsa): /Users/kingboy/.ssh/gitlab_id_rsa</span><br></pre></td></tr></table></figure><br>注意：输入的是钥匙的位置和名称。github和gitlab是不同的。<p></p><p>（3）完成后会在~/.ssh/目录下生成以下文件:<br></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">github_id_rsa</span><br><span class="line">github_id_rsa.pub</span><br><span class="line">gitlab_id_rsa</span><br><span class="line">gitlab_id_rsa.pub</span><br></pre></td></tr></table></figure><p></p><h2 id="三、添加识别-SSH-keys-新的私钥"><a href="#三、添加识别-SSH-keys-新的私钥" class="headerlink" title="三、添加识别 SSH keys 新的私钥"></a>三、添加识别 SSH keys 新的私钥</h2><p>默认只读取 id_rsa，为了让 SSH 识别新的私钥，需要将新的私钥加入到 SSH agent 中<br></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ ssh-agent bash</span><br><span class="line">$ ssh-add ~/.ssh/github_id_rsa</span><br><span class="line">$ ssh-add ~/.ssh/gitlab_id_rsa</span><br></pre></td></tr></table></figure><p></p><h2 id="四、多账号必须配置-config-文件"><a href="#四、多账号必须配置-config-文件" class="headerlink" title="四、多账号必须配置 config 文件"></a>四、多账号必须配置 config 文件</h2><p>若无 config 文件，则需创建 config 文件<br></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ touch ~/.ssh/config        <span class="comment"># 创建config文件</span></span><br></pre></td></tr></table></figure><br>config 里需要填的内容<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">#Default gitHub user Self</span><br><span class="line">Host github.com</span><br><span class="line">HostName github.com</span><br><span class="line">User github</span><br><span class="line">IdentityFile ~&#x2F;.ssh&#x2F;github_id_rsa</span><br><span class="line"></span><br><span class="line">#Add gitLab user</span><br><span class="line">Host git@git.gitlab.com</span><br><span class="line">HostName git@git.gitlab.com</span><br><span class="line">User gitlab</span><br><span class="line">IdentityFile ~&#x2F;.ssh&#x2F;gitlab_id_rsa</span><br></pre></td></tr></table></figure><p></p><h2 id="五、在github和gitlab网站添加ssh"><a href="#五、在github和gitlab网站添加ssh" class="headerlink" title="五、在github和gitlab网站添加ssh"></a>五、在github和gitlab网站添加ssh</h2><h2 id="六、测试是否连接成功"><a href="#六、测试是否连接成功" class="headerlink" title="六、测试是否连接成功"></a>六、测试是否连接成功</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 测试github</span></span><br><span class="line">$ ssh -T git@github.com</span><br><span class="line"> </span><br><span class="line"><span class="comment"># 测试gitlab</span></span><br><span class="line">$ ssh -T git@gitlab.com</span><br></pre></td></tr></table></figure><h2 id="七、修改commit中的author"><a href="#七、修改commit中的author" class="headerlink" title="七、修改commit中的author"></a>七、修改commit中的author</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/bash </span></span><br><span class="line">git filter-branch  --force --env-filter <span class="string">' </span></span><br><span class="line"><span class="string">    #如果Git用户名等于老的Git用户名 snaildev</span></span><br><span class="line"><span class="string">    if [ "$GIT_COMMITTER_NAME" = "snaildev" ] || [ "$GIT_AUTHOR_EMAIL" = "snaildev@outlook.com" ]; </span></span><br><span class="line"><span class="string">    then </span></span><br><span class="line"><span class="string">      #替换用户名为新的用户名，替换邮箱为正确的邮箱</span></span><br><span class="line"><span class="string">      GIT_AUTHOR_NAME="snaildev"; </span></span><br><span class="line"><span class="string">      GIT_AUTHOR_EMAIL="snailtem@gmail.com"; </span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">      #替换提交的用户名为新的用户名，替换提交的邮箱为正确的邮箱</span></span><br><span class="line"><span class="string">      GIT_COMMITTER_NAME="snaildev"; </span></span><br><span class="line"><span class="string">      GIT_COMMITTER_EMAIL="snailtem@gmail.com"; </span></span><br><span class="line"><span class="string">    fi </span></span><br><span class="line"><span class="string">'</span> --tag-name-filter cat -- --branches --tags</span><br><span class="line"></span><br><span class="line">git push -f</span><br><span class="line"></span><br><span class="line"><span class="comment">#如果拉取的时候出现历史冲突，使用下面指令拉取</span></span><br><span class="line">git pull --allow-unrelated-histories</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;/images/GitLab-vs-GitHub-vs-bitbucket-1.jpg&quot; alt=&quot;git platform&quot;&gt;&lt;/p&gt;&lt;p&gt;日常使用 git 作为仓库使用时，会遇到以下情况：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;有两个 github 账号（至少两个），一台电脑同时连接这两个账号进行维护&lt;/li&gt;&lt;li&gt;私人 github 账号，公司 gitlab 账号&lt;/li&gt;&lt;/ul&gt;
    
    </summary>
    
    
      <category term="smnpo" scheme="http://blog.develophelper.com/categories/smnpo/"/>
    
      <category term="github" scheme="http://blog.develophelper.com/categories/smnpo/github/"/>
    
      <category term="ssh-key" scheme="http://blog.develophelper.com/categories/smnpo/github/ssh-key/"/>
    
    
      <category term="smnpo" scheme="http://blog.develophelper.com/tags/smnpo/"/>
    
      <category term="github" scheme="http://blog.develophelper.com/tags/github/"/>
    
      <category term="ssh-key" scheme="http://blog.develophelper.com/tags/ssh-key/"/>
    
  </entry>
  
  <entry>
    <title>maven使用之settings.xml配置</title>
    <link href="http://blog.develophelper.com/article/smnpo-maven-repository-settings.html"/>
    <id>http://blog.develophelper.com/article/smnpo-maven-repository-settings.html</id>
    <published>2019-05-04T14:33:15.000Z</published>
    <updated>2020-02-15T15:23:10.033Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/images/Maven_logo.svg_.png" alt="maven"></p><p>本 maven settings.xml相关配置是在做smnpo微服务相关组件时进行配置，包含nexus3 maven 私有仓库的搭建以及账号密码权限相关配置，具体可参考xml配置的详细信息。</p><a id="more"></a><div class="table-container"><table><thead><tr><th>日期</th><th>更新内容</th></tr></thead><tbody><tr><td>20190507</td><td>新增pom.xml中添加repository不生效的解决方案</td></tr><tr><td>20190509</td><td>将私有maven库改用到阿里云效并更新完整settings.xml</td></tr></tbody></table></div><h2 id="20190509"><a href="#20190509" class="headerlink" title="20190509"></a>20190509</h2><p><img src="/images/centos.png" alt="centos"><br>今天抽空看了一下centos的cpu和mem使用情况，发现自建maven仓库造成内存消耗严重，于是将私有仓库切换到<strong>阿里云效</strong><br></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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">repositories</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">repository</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;<span class="name">id</span>&gt;</span>rdc-releases<span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;<span class="name">url</span>&gt;</span>https://repo.rdc.aliyun.com/repository/71943-release-LWfjuf/<span class="tag">&lt;/<span class="name">url</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;<span class="name">releases</span>&gt;</span></span><br><span class="line">             <span class="tag">&lt;<span class="name">enabled</span>&gt;</span>true<span class="tag">&lt;/<span class="name">enabled</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;/<span class="name">releases</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;<span class="name">snapshots</span>&gt;</span></span><br><span class="line">             <span class="tag">&lt;<span class="name">enabled</span>&gt;</span>false<span class="tag">&lt;/<span class="name">enabled</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;/<span class="name">snapshots</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;/<span class="name">repository</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">repository</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;<span class="name">id</span>&gt;</span>rdc-snapshots<span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;<span class="name">url</span>&gt;</span>https://repo.rdc.aliyun.com/repository/71943-snapshot-gMBvZN/<span class="tag">&lt;/<span class="name">url</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;<span class="name">releases</span>&gt;</span></span><br><span class="line">             <span class="tag">&lt;<span class="name">enabled</span>&gt;</span>false<span class="tag">&lt;/<span class="name">enabled</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;/<span class="name">releases</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;<span class="name">snapshots</span>&gt;</span></span><br><span class="line">             <span class="tag">&lt;<span class="name">enabled</span>&gt;</span>true<span class="tag">&lt;/<span class="name">enabled</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;/<span class="name">snapshots</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;/<span class="name">repository</span>&gt;</span></span><br><span class="line"> <span class="tag">&lt;/<span class="name">repositories</span>&gt;</span></span><br></pre></td></tr></table></figure><p></p><h2 id="20190507"><a href="#20190507" class="headerlink" title="20190507"></a>20190507</h2><p>项目pom.xml中的repository配置不生效的问题解析<br></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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">mirror</span>&gt;</span>         </span><br><span class="line">  <span class="tag">&lt;<span class="name">id</span>&gt;</span>alimaven<span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">name</span>&gt;</span>aliyun maven<span class="tag">&lt;/<span class="name">name</span>&gt;</span> </span><br><span class="line">  <span class="tag">&lt;<span class="name">url</span>&gt;</span>http://maven.aliyun.com/nexus/content/groups/public/<span class="tag">&lt;/<span class="name">url</span>&gt;</span> </span><br><span class="line">  <span class="tag">&lt;<span class="name">mirrorOf</span>&gt;</span>central<span class="tag">&lt;/<span class="name">mirrorOf</span>&gt;</span> </span><br><span class="line"><span class="tag">&lt;/<span class="name">mirror</span>&gt;</span></span><br></pre></td></tr></table></figure><br>错误就出在mirrorOf节点了，如果写*会覆盖掉所有的，不管是哪个repository，最后都被这个镜像所mirror掉了，导致pom文件中的repository不生效了。<br>解决方案也很简单，把这个mirrorOf改掉就好了。具体修改建议参考maven官方说明：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">* = everything</span><br><span class="line">external:* = everything not on the localhost and not file based.</span><br><span class="line">repo,repo1 = repo or repo1</span><br><span class="line">*,!repo1 = everything except repo1</span><br></pre></td></tr></table></figure><p></p><h2 id="20190505"><a href="#20190505" class="headerlink" title="20190505"></a>20190505</h2><p>完整settings.xml如下：<br></p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">settings</span> <span class="attr">xmlns</span>=<span class="string">"http://maven.apache.org/SETTINGS/1.0.0"</span></span></span><br><span class="line"><span class="tag">          <span class="attr">xmlns:xsi</span>=<span class="string">"http://www.w3.org/2001/XMLSchema-instance"</span></span></span><br><span class="line"><span class="tag">          <span class="attr">xsi:schemaLocation</span>=<span class="string">"http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">mirrors</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">mirror</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">id</span>&gt;</span>mirror<span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">mirrorOf</span>&gt;</span>!rdc-releases,!rdc-snapshots<span class="tag">&lt;/<span class="name">mirrorOf</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">name</span>&gt;</span>mirror<span class="tag">&lt;/<span class="name">name</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">url</span>&gt;</span>http://maven.aliyun.com/nexus/content/groups/public<span class="tag">&lt;/<span class="name">url</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">mirror</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;/<span class="name">mirrors</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">servers</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">server</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">id</span>&gt;</span>rdc-releases<span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">username</span>&gt;</span>vC1AQt<span class="tag">&lt;/<span class="name">username</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">password</span>&gt;</span>联系SnailDev<span class="tag">&lt;/<span class="name">password</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">server</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">server</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">id</span>&gt;</span>rdc-snapshots<span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">username</span>&gt;</span>vC1AQt<span class="tag">&lt;/<span class="name">username</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">password</span>&gt;</span>联系SnailDev<span class="tag">&lt;/<span class="name">password</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">server</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">servers</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">profiles</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">profile</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">id</span>&gt;</span>nexus<span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">repositories</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">repository</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">id</span>&gt;</span>central<span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">url</span>&gt;</span>http://maven.aliyun.com/nexus/content/groups/public<span class="tag">&lt;/<span class="name">url</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">releases</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">enabled</span>&gt;</span>true<span class="tag">&lt;/<span class="name">enabled</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;/<span class="name">releases</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">snapshots</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">enabled</span>&gt;</span>false<span class="tag">&lt;/<span class="name">enabled</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;/<span class="name">snapshots</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">repository</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">repository</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">id</span>&gt;</span>snapshots<span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">url</span>&gt;</span>http://maven.aliyun.com/nexus/content/groups/public<span class="tag">&lt;/<span class="name">url</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">releases</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">enabled</span>&gt;</span>false<span class="tag">&lt;/<span class="name">enabled</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;/<span class="name">releases</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">snapshots</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">enabled</span>&gt;</span>true<span class="tag">&lt;/<span class="name">enabled</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;/<span class="name">snapshots</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">repository</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">repository</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">id</span>&gt;</span>rdc-releases<span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">url</span>&gt;</span>https://repo.rdc.aliyun.com/repository/71943-release-LWfjuf/<span class="tag">&lt;/<span class="name">url</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">releases</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">enabled</span>&gt;</span>true<span class="tag">&lt;/<span class="name">enabled</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;/<span class="name">releases</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">snapshots</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">enabled</span>&gt;</span>false<span class="tag">&lt;/<span class="name">enabled</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;/<span class="name">snapshots</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">repository</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">repository</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">id</span>&gt;</span>rdc-snapshots<span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">url</span>&gt;</span>https://repo.rdc.aliyun.com/repository/71943-snapshot-gMBvZN/<span class="tag">&lt;/<span class="name">url</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">releases</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">enabled</span>&gt;</span>false<span class="tag">&lt;/<span class="name">enabled</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;/<span class="name">releases</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">snapshots</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">enabled</span>&gt;</span>true<span class="tag">&lt;/<span class="name">enabled</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;/<span class="name">snapshots</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">repository</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">repositories</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">pluginRepositories</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">pluginRepository</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">id</span>&gt;</span>central<span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">url</span>&gt;</span>http://maven.aliyun.com/nexus/content/groups/public<span class="tag">&lt;/<span class="name">url</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">releases</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">enabled</span>&gt;</span>true<span class="tag">&lt;/<span class="name">enabled</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;/<span class="name">releases</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">snapshots</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">enabled</span>&gt;</span>false<span class="tag">&lt;/<span class="name">enabled</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;/<span class="name">snapshots</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">pluginRepository</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">pluginRepository</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">id</span>&gt;</span>snapshots<span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">url</span>&gt;</span>http://maven.aliyun.com/nexus/content/groups/public<span class="tag">&lt;/<span class="name">url</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">releases</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">enabled</span>&gt;</span>false<span class="tag">&lt;/<span class="name">enabled</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;/<span class="name">releases</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">snapshots</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">enabled</span>&gt;</span>true<span class="tag">&lt;/<span class="name">enabled</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;/<span class="name">snapshots</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">pluginRepository</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">pluginRepository</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">id</span>&gt;</span>rdc-releases<span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">url</span>&gt;</span>https://repo.rdc.aliyun.com/repository/71943-release-LWfjuf/<span class="tag">&lt;/<span class="name">url</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">releases</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">enabled</span>&gt;</span>true<span class="tag">&lt;/<span class="name">enabled</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;/<span class="name">releases</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">snapshots</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">enabled</span>&gt;</span>false<span class="tag">&lt;/<span class="name">enabled</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;/<span class="name">snapshots</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">pluginRepository</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">pluginRepository</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">id</span>&gt;</span>rdc-snapshots<span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">url</span>&gt;</span>https://repo.rdc.aliyun.com/repository/71943-snapshot-gMBvZN/<span class="tag">&lt;/<span class="name">url</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">releases</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">enabled</span>&gt;</span>false<span class="tag">&lt;/<span class="name">enabled</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;/<span class="name">releases</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">snapshots</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">enabled</span>&gt;</span>true<span class="tag">&lt;/<span class="name">enabled</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;/<span class="name">snapshots</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">pluginRepository</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">pluginRepositories</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">profile</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;/<span class="name">profiles</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">activeProfiles</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">activeProfile</span>&gt;</span>nexus<span class="tag">&lt;/<span class="name">activeProfile</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;/<span class="name">activeProfiles</span>&gt;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">settings</span>&gt;</span></span><br></pre></td></tr></table></figure><p></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;/images/Maven_logo.svg_.png&quot; alt=&quot;maven&quot;&gt;&lt;/p&gt;&lt;p&gt;本 maven settings.xml相关配置是在做smnpo微服务相关组件时进行配置，包含nexus3 maven 私有仓库的搭建以及账号密码权限相关配置，具体可参考xml配置的详细信息。&lt;/p&gt;
    
    </summary>
    
    
      <category term="smnpo" scheme="http://blog.develophelper.com/categories/smnpo/"/>
    
      <category term="maven" scheme="http://blog.develophelper.com/categories/smnpo/maven/"/>
    
    
      <category term="smnpo" scheme="http://blog.develophelper.com/tags/smnpo/"/>
    
      <category term="maven" scheme="http://blog.develophelper.com/tags/maven/"/>
    
  </entry>
  
  <entry>
    <title>SpringBoot实战（六）Mybatis</title>
    <link href="http://blog.develophelper.com/article/spring-springboot-mybatis.html"/>
    <id>http://blog.develophelper.com/article/spring-springboot-mybatis.html</id>
    <published>2018-09-06T13:13:28.000Z</published>
    <updated>2020-02-15T15:23:10.033Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/images/spring-springboot-1.jpg" alt="spring-springboot-thymeleaf"></p><p>ORM框架的本质就是简化编程中操作数据库的编码，发展到现在基本上就剩两家了，一个是宣称可以不用写一句SQL的hibernate，一个是可以灵活调试动态sql的mybatis，两者各有特点，在企业级系统开发中可以根据需求灵活使用。发现一个有趣的现象：传统企业大都喜欢使用hibernate，互联网行业通常使用mybatis。<br><a id="more"></a></p><p>Hibernate特点就是所有的sql都用Java代码来生成，不用跳出程序去写（看）sql，有着编程的完整性，发展到最顶端就是spring data jpa这种模式了，基本上根据方法名就可以生成对应的sql了。</p><p>Mybatis初期使用比较麻烦，需要各种配置文件、实体类、dao层映射关联、还有一大堆其他配置。当然mybatis也发现了这种弊端，初期开发了<a href="https://github.com/mybatis/generator" target="_blank" rel="noopener">generator</a>可以根据表结果自动生成实体类、配置文件和dao层代码，可以减轻一部分开发量；后期也进行了大量的优化可以使用注解，自动管理dao层和配置文件等，发展到最顶端就是今天讲的这种模式，mybatis-spring-boot-starter就是springboot+mybatis可以完全注解不用配置文件，也可以简单配置轻松上手。</p><h2 id="mybatis-spring-boot-starter"><a href="#mybatis-spring-boot-starter" class="headerlink" title="mybatis-spring-boot-starter"></a>mybatis-spring-boot-starter</h2><p>官方说明：MyBatis Spring-Boot-Starter will help you use MyBatis with Spring Boot</p><p>其实就是MyBatis看spring boot这么火热也开发出一套解决方案来凑凑热闹，但这一凑却是解决了很多问题，使用起来也却是顺畅了很多。mybatis-spring-boot-starter主要有两种解决方案，一种是使用注解解决一切问题，一种是简化后的老传统。</p><p>当然，任何模式都需要在pom文件中首先引入mybatis-spring-boot-starter的依赖。<br></p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.mybatis.spring.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>mybatis-spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><br>好了，现在分别介绍两种开发模式<p></p><h3 id="无配置文件注解版"><a href="#无配置文件注解版" class="headerlink" title="无配置文件注解版"></a>无配置文件注解版</h3><p>就是一切使用注解搞定。</p><h4 id="1-添加相关maven依赖"><a href="#1-添加相关maven依赖" class="headerlink" title="1. 添加相关maven依赖"></a>1. 添加相关maven依赖</h4><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-test<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">scope</span>&gt;</span>test<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-web<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>mysql<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>mysql-connector-java<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.mybatis.spring.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>mybatis-spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br></pre></td></tr></table></figure><h4 id="2-application-properties-添加相关配置"><a href="#2-application-properties-添加相关配置" class="headerlink" title="2. application.properties 添加相关配置"></a>2. application.properties 添加相关配置</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">mybatis.type-aliases-package&#x3D;com.example.springbootmybatis.entity</span><br><span class="line"></span><br><span class="line">spring.datasource.driver-class-name&#x3D;com.mysql.jdbc.Driver</span><br><span class="line">spring.datasource.url&#x3D;jdbc:mysql:&#x2F;&#x2F;localhost:3306&#x2F;test1?useUnicode&#x3D;true&amp;characterEncoding&#x3D;utf-8</span><br><span class="line">spring.datasource.username&#x3D;root</span><br><span class="line">spring.datasource.password&#x3D;123456</span><br></pre></td></tr></table></figure><p>spring boot 会自动加载spring.datasource.*相关配置，数据源就会自动注入到sqlSessionFactory中，sqlSessionFactory会自动注入到Mapper中，对了你一切都不用管了，直接拿起来用就可以了。</p><p>在启动类中添加对Mapper包扫描@MapperScan<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></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="meta">@MapperScan</span>()</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SpringBootMybatisApplication</span> </span>&#123;</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">    SpringApplication.run(SpringBootMybatisApplication<span class="class">.<span class="keyword">class</span>, <span class="title">args</span>)</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>或者直接在Mapper类上面添加注解<code>@Mapper</code>,建议使用上面这种，不然每个Mapper都加个注解也挺麻烦的。<p></p><h4 id="3-开发Mapper"><a href="#3-开发Mapper" class="headerlink" title="3. 开发Mapper"></a>3. 开发Mapper</h4><p>第三步是关键的一步，所有的sql生成都在这里<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><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">UserMapper</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Select</span>(<span class="string">"SELECT * FROM users"</span>)</span><br><span class="line">    <span class="meta">@Results</span>(&#123;</span><br><span class="line">            <span class="meta">@Result</span>(property = <span class="string">"userSex"</span>, column = <span class="string">"user_sex"</span>, javaType = UserSexEnum<span class="class">.<span class="keyword">class</span>),</span></span><br><span class="line"><span class="class">            @<span class="title">Result</span>(<span class="title">property</span> </span>= <span class="string">"nickName"</span>, column = <span class="string">"nick_name"</span>)</span><br><span class="line">    &#125;)</span><br><span class="line">    <span class="function">List&lt;UserEntity&gt; <span class="title">getAll</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Select</span>(<span class="string">"SELECT * FROM users WHERE id = #&#123;id&#125;"</span>)</span><br><span class="line">    <span class="meta">@Results</span>(&#123;</span><br><span class="line">            <span class="meta">@Result</span>(property = <span class="string">"userSex"</span>, column = <span class="string">"user_sex"</span>, javaType = UserSexEnum<span class="class">.<span class="keyword">class</span>),</span></span><br><span class="line"><span class="class">            @<span class="title">Result</span>(<span class="title">property</span> </span>= <span class="string">"nickName"</span>, column = <span class="string">"nick_name"</span>)</span><br><span class="line">    &#125;)</span><br><span class="line">    <span class="function">UserEntity <span class="title">getOne</span><span class="params">(Long id)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Insert</span>(<span class="string">"INSERT INTO users(userName,passWord,user_sex) VALUES(#&#123;userName&#125;,#&#123;passWord&#125;,#&#123;userSex&#125;)"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">insert</span><span class="params">(UserEntity user)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Update</span>(<span class="string">"UPDATE users SET userName=#&#123;userName&#125;,nick_name=#&#123;nickName&#125; WHERE id=#&#123;id&#125;"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">update</span><span class="params">(UserEntity user)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Delete</span>(<span class="string">"DELETE FROM user WHERE id = #&#123;id&#125;"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">delete</span><span class="params">(Long id)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>为了更接近生产特地将user_sex,nick_name两个属性在数据库中加了下划线和实体类属性名不一致，另外user_sex使用了枚举<p></p><blockquote><ul><li>@Select 是查询类的注解，所有的查询均使用这个；</li><li>@Result 修饰返回的结果集，关联实体类属性和数据库字段一一对应，如果实体类属性和数据库属性名保持一致，就不需要这个属性来修饰；</li><li>@Insert 插入数据库使用，直接传入实体类会自动解析属性到对应的值；</li><li>@Update 负责修改，也可以直接传入对象</li><li>@Delete 负责删除<br><a href="http://www.mybatis.org/mybatis-3/zh/java-api.html" target="_blank" rel="external nofollow noopener noreferrer">了解更多属性参考这里</a></li></ul><p><strong>注意，使用#符号和$符号的不同</strong><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// This example creates a prepared statement, something like select * from teacher where name = ?;</span></span><br><span class="line"><span class="meta">@Select</span>(<span class="string">"Select * from teacher where name = #&#123;name&#125;"</span>)</span><br><span class="line"><span class="function">Teacher <span class="title">selectTeachForGivenName</span><span class="params">(@Param(<span class="string">"name"</span>)</span> String name)</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// This example creates n inlined statement, something like select * from teacher where name = 'someName';</span></span><br><span class="line"><span class="meta">@Select</span>(<span class="string">"Select * from teacher where name = '$&#123;name&#125;'"</span>)</span><br><span class="line"><span class="function">Teacher <span class="title">selectTeachForGivenName</span><span class="params">(@Param(<span class="string">"name"</span>)</span> String name)</span>;</span><br></pre></td></tr></table></figure><p></p></blockquote><h4 id="4-使用"><a href="#4-使用" class="headerlink" title="4. 使用"></a>4. 使用</h4><p>上面三步就基本完成了相关的dao层开发，使用的时候当作普通的类注入就可以了<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><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@RunWith</span>(SpringRunner<span class="class">.<span class="keyword">class</span>)</span></span><br><span class="line"><span class="class">@<span class="title">SpringBootTest</span></span></span><br><span class="line"><span class="class"><span class="title">public</span> <span class="title">class</span> <span class="title">UserMapperTest</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> UserMapper userMapper;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testInsert</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        userMapper.insert(<span class="keyword">new</span> UserEntity(<span class="string">"aa"</span>, <span class="string">"a123456"</span>, UserSexEnum.MAN));</span><br><span class="line">        userMapper.insert(<span class="keyword">new</span> UserEntity(<span class="string">"bb"</span>, <span class="string">"b123456"</span>, UserSexEnum.WOMAN));</span><br><span class="line">        userMapper.insert(<span class="keyword">new</span> UserEntity(<span class="string">"cc"</span>, <span class="string">"b123456"</span>, UserSexEnum.WOMAN));</span><br><span class="line"></span><br><span class="line">        Assert.assertEquals(<span class="number">3</span>, userMapper.getAll().size());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testQuery</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        List&lt;UserEntity&gt; users = userMapper.getAll();</span><br><span class="line">        System.out.println(users.toString());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testUpdate</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        UserEntity user = userMapper.getOne(<span class="keyword">new</span> Long(<span class="number">30</span>));</span><br><span class="line">        System.out.println(user.toString());</span><br><span class="line">        user.setNickName(<span class="string">"snaildev"</span>);</span><br><span class="line">        userMapper.update(user);</span><br><span class="line">        Assert.assertTrue(<span class="string">"snaildev"</span>.equals(userMapper.getOne(<span class="keyword">new</span> Long(<span class="number">30</span>)).getNickName()));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><h3 id="极简xml版本"><a href="#极简xml版本" class="headerlink" title="极简xml版本"></a>极简xml版本</h3><p>极简xml版本保持映射文件的老传统，优点主要体现在不需要实现dao的是实现层，系统会自动根据方法名在映射文件中找到对应的sql。</p><h4 id="1-配置"><a href="#1-配置" class="headerlink" title="1. 配置"></a>1. 配置</h4><p>pom文件和上面第一种方式一致，只是application.properties新增以下配置<br></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">mybatis.config-location&#x3D;classpath:mybatis&#x2F;mybatis-config.xml</span><br><span class="line">mybatis.mapper-locations&#x3D;classpath:mybatis&#x2F;mapper&#x2F;*.xml</span><br></pre></td></tr></table></figure><br>指定了mybatis基础配置文件和实体类映射文件的地址<p></p><p>mybatis-config.xml配置<br></p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version="1.0" encoding="UTF-8" ?&gt;</span></span><br><span class="line"><span class="meta">&lt;!DOCTYPE <span class="meta-keyword">configuration</span> <span class="meta-keyword">PUBLIC</span> <span class="meta-string">"-//mybatis.org//DTD Config 3.0//EN"</span> <span class="meta-string">"http://mybatis.org/dtd/mybatis-3-config.dtd"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">configuration</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">typeAliases</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">typeAlias</span> <span class="attr">alias</span>=<span class="string">"Integer"</span> <span class="attr">type</span>=<span class="string">"java.lang.Integer"</span> /&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">typeAlias</span> <span class="attr">alias</span>=<span class="string">"Long"</span> <span class="attr">type</span>=<span class="string">"java.lang.Long"</span> /&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">typeAlias</span> <span class="attr">alias</span>=<span class="string">"HashMap"</span> <span class="attr">type</span>=<span class="string">"java.util.HashMap"</span> /&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">typeAlias</span> <span class="attr">alias</span>=<span class="string">"LinkedHashMap"</span> <span class="attr">type</span>=<span class="string">"java.util.LinkedHashMap"</span> /&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">typeAlias</span> <span class="attr">alias</span>=<span class="string">"ArrayList"</span> <span class="attr">type</span>=<span class="string">"java.util.ArrayList"</span> /&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">typeAlias</span> <span class="attr">alias</span>=<span class="string">"LinkedList"</span> <span class="attr">type</span>=<span class="string">"java.util.LinkedList"</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">typeAliases</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">configuration</span>&gt;</span></span><br></pre></td></tr></table></figure><br>这里也可以添加一些mybatis基础的设置<p></p><h4 id="2-添加User的映射文件"><a href="#2-添加User的映射文件" class="headerlink" title="2. 添加User的映射文件"></a>2. 添加User的映射文件</h4><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version="1.0" encoding="UTF-8" ?&gt;</span></span><br><span class="line"><span class="meta">&lt;!DOCTYPE <span class="meta-keyword">mapper</span> <span class="meta-keyword">PUBLIC</span> <span class="meta-string">"-//mybatis.org//DTD Mapper 3.0//EN"</span> <span class="meta-string">"http://mybatis.org/dtd/mybatis-3-mapper.dtd"</span> &gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">mapper</span> <span class="attr">namespace</span>=<span class="string">"com.example.springbootmybatis.mapper.UserMapper"</span> &gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">resultMap</span> <span class="attr">id</span>=<span class="string">"BaseResultMap"</span> <span class="attr">type</span>=<span class="string">"com.example.springbootmybatis.entity.UserEntity"</span> &gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">id</span> <span class="attr">column</span>=<span class="string">"id"</span> <span class="attr">property</span>=<span class="string">"id"</span> <span class="attr">jdbcType</span>=<span class="string">"BIGINT"</span> /&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">result</span> <span class="attr">column</span>=<span class="string">"userName"</span> <span class="attr">property</span>=<span class="string">"userName"</span> <span class="attr">jdbcType</span>=<span class="string">"VARCHAR"</span> /&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">result</span> <span class="attr">column</span>=<span class="string">"passWord"</span> <span class="attr">property</span>=<span class="string">"passWord"</span> <span class="attr">jdbcType</span>=<span class="string">"VARCHAR"</span> /&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">result</span> <span class="attr">column</span>=<span class="string">"user_sex"</span> <span class="attr">property</span>=<span class="string">"userSex"</span> <span class="attr">javaType</span>=<span class="string">"com.example.springbootmybatis.enums.UserSexEnum"</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">result</span> <span class="attr">column</span>=<span class="string">"nick_name"</span> <span class="attr">property</span>=<span class="string">"nickName"</span> <span class="attr">jdbcType</span>=<span class="string">"VARCHAR"</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">resultMap</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">sql</span> <span class="attr">id</span>=<span class="string">"Base_Column_List"</span> &gt;</span></span><br><span class="line">        id, userName, passWord, user_sex, nick_name</span><br><span class="line">    <span class="tag">&lt;/<span class="name">sql</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">"getAll"</span> <span class="attr">resultMap</span>=<span class="string">"BaseResultMap"</span>  &gt;</span></span><br><span class="line">        SELECT</span><br><span class="line">        <span class="tag">&lt;<span class="name">include</span> <span class="attr">refid</span>=<span class="string">"Base_Column_List"</span> /&gt;</span></span><br><span class="line">        FROM users</span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">"getOne"</span> <span class="attr">parameterType</span>=<span class="string">"java.lang.Long"</span> <span class="attr">resultMap</span>=<span class="string">"BaseResultMap"</span> &gt;</span></span><br><span class="line">        SELECT</span><br><span class="line">        <span class="tag">&lt;<span class="name">include</span> <span class="attr">refid</span>=<span class="string">"Base_Column_List"</span> /&gt;</span></span><br><span class="line">        FROM users</span><br><span class="line">        WHERE id = #&#123;id&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">insert</span> <span class="attr">id</span>=<span class="string">"insert"</span> <span class="attr">parameterType</span>=<span class="string">"com.example.springbootmybatis.entity.UserEntity"</span> &gt;</span></span><br><span class="line">       INSERT INTO</span><br><span class="line">       users</span><br><span class="line">       (userName,passWord,user_sex)</span><br><span class="line">       VALUES</span><br><span class="line">       (#&#123;userName&#125;, #&#123;passWord&#125;, #&#123;userSex&#125;)</span><br><span class="line">    <span class="tag">&lt;/<span class="name">insert</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">update</span> <span class="attr">id</span>=<span class="string">"update"</span> <span class="attr">parameterType</span>=<span class="string">"com.example.springbootmybatis.entity.UserEntity"</span> &gt;</span></span><br><span class="line">        UPDATE</span><br><span class="line">        users</span><br><span class="line">        SET</span><br><span class="line">        <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">"userName != null"</span>&gt;</span>userName = #&#123;userName&#125;,<span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">"passWord != null"</span>&gt;</span>passWord = #&#123;passWord&#125;,<span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">        nick_name = #&#123;nickName&#125;</span><br><span class="line">        WHERE</span><br><span class="line">        id = #&#123;id&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">update</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">delete</span> <span class="attr">id</span>=<span class="string">"delete"</span> <span class="attr">parameterType</span>=<span class="string">"java.lang.Long"</span> &gt;</span></span><br><span class="line">       DELETE FROM</span><br><span class="line">        users</span><br><span class="line">       WHERE</span><br><span class="line">        id =#&#123;id&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">delete</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">mapper</span>&gt;</span></span><br></pre></td></tr></table></figure><p>其实就是把上个版本中Mapper的sql搬到了这里的xml中了</p><h4 id="3-编写Dao层的代码"><a href="#3-编写Dao层的代码" class="headerlink" title="3. 编写Dao层的代码"></a>3. 编写Dao层的代码</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></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">UserMapper</span> </span>&#123;</span><br><span class="line"><span class="comment">//    @Select("SELECT * FROM users")</span></span><br><span class="line"><span class="comment">//    @Results(&#123;</span></span><br><span class="line"><span class="comment">//            @Result(property = "userSex", column = "user_sex", javaType = UserSexEnum.class),</span></span><br><span class="line"><span class="comment">//            @Result(property = "nickName", column = "nick_name")</span></span><br><span class="line"><span class="comment">//    &#125;)</span></span><br><span class="line">    <span class="function">List&lt;UserEntity&gt; <span class="title">getAll</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">//    @Select("SELECT * FROM users WHERE id = #&#123;id&#125;")</span></span><br><span class="line"><span class="comment">//    @Results(&#123;</span></span><br><span class="line"><span class="comment">//            @Result(property = "userSex", column = "user_sex", javaType = UserSexEnum.class),</span></span><br><span class="line"><span class="comment">//            @Result(property = "nickName", column = "nick_name")</span></span><br><span class="line"><span class="comment">//    &#125;)</span></span><br><span class="line">    <span class="function">UserEntity <span class="title">getOne</span><span class="params">(Long id)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">//    @Insert("INSERT INTO users(userName,passWord,user_sex) VALUES(#&#123;userName&#125;,#&#123;passWord&#125;,#&#123;userSex&#125;)")</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">insert</span><span class="params">(UserEntity user)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">//    @Update("UPDATE users SET userName=#&#123;userName&#125;,nick_name=#&#123;nickName&#125; WHERE id=#&#123;id&#125;")</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">update</span><span class="params">(UserEntity user)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">//    @Delete("DELETE FROM user WHERE id = #&#123;id&#125;")</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">delete</span><span class="params">(Long id)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>对比上一种方式，这里只剩接口方法</p><h4 id="4-使用-1"><a href="#4-使用-1" class="headerlink" title="4. 使用"></a>4. 使用</h4><p>使用方式和第一种方式没什么区别，可以参考第一种代码实现。</p><h3 id="如何选择"><a href="#如何选择" class="headerlink" title="如何选择"></a>如何选择</h3><p>两种模式各有特点，注解版适合简单快速的模式，其实像现在流行的微服务模式，一个微服务就会对应一个自己的数据库，多表连接查询的需求会大大的降低，会越来越适合这种模式了。</p><p>老传统模式即xml配置文件的方式，更适合大型项目，可以灵活的动态生成SQL，方便调整SQL，也有痛痛快快，洋洋洒洒的写SQL的感觉。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;/images/spring-springboot-1.jpg&quot; alt=&quot;spring-springboot-thymeleaf&quot;&gt;&lt;/p&gt;&lt;p&gt;ORM框架的本质就是简化编程中操作数据库的编码，发展到现在基本上就剩两家了，一个是宣称可以不用写一句SQL的hibernate，一个是可以灵活调试动态sql的mybatis，两者各有特点，在企业级系统开发中可以根据需求灵活使用。发现一个有趣的现象：传统企业大都喜欢使用hibernate，互联网行业通常使用mybatis。&lt;br&gt;
    
    </summary>
    
    
      <category term="SpringBoot" scheme="http://blog.develophelper.com/categories/springboot/"/>
    
    
      <category term="SpringBoot" scheme="http://blog.develophelper.com/tags/springboot/"/>
    
      <category term="Mybatis" scheme="http://blog.develophelper.com/tags/mybatis/"/>
    
  </entry>
  
  <entry>
    <title>SpringBoot实战（五）Jpa</title>
    <link href="http://blog.develophelper.com/article/spring-springboot-jpa.html"/>
    <id>http://blog.develophelper.com/article/spring-springboot-jpa.html</id>
    <published>2018-09-05T15:27:05.000Z</published>
    <updated>2020-02-15T15:23:10.033Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/images/spring-springboot-1.jpg" alt="spring-springboot-thymeleaf"></p><p>JPA(Java Persistence API) 是Sun官方提出的Java持久化规范。它为Java开发人员提供了一种对象/关联映射工具来管理Java应用中的关系数据。它的出现主要是为了简化现有的持久化开发工作和整合ORM技术，结束现在Hibernate、TopLink、JDO等ORM框架各自为营的局面。值得注意的是，JPA是在充分吸收了现有Hibernate、TopLink、JDO等ORM框架的长处发展而来，具有易于使用，伸缩性强等特点。从目前的开发社区的反应上看，JPA受到了极大的支持和赞扬，其中就包括了Spring和EJB3.0的开发团队。<br><a id="more"></a></p><blockquote><p>JPA是一套规范，不是一套产品，而Hibernate、TopLink、JDO等是一套产品。<br>如果说这些产品实现了JPA规范，那么我们就可以叫它们为JPA的实现产品。</p></blockquote><h2 id="Spring-Data-JPA"><a href="#Spring-Data-JPA" class="headerlink" title="Spring Data JPA"></a>Spring Data JPA</h2><p>Spring Data JPA 是 Spring 基于ORM框架、JPA规范的基础封装的一套JPA应用框架，可使开发者用极简的代码即可实现对数据的访问和操作。它提供了包括增删改查等在内的常用功能，且易于扩展！学习并使用Spring Data JPA可以极大提高开发效率！</p><blockquote><p>Spring Data JPA让我们解脱了DAO层的操作，基本上所有的CRUD都可以依赖于它来实现。</p></blockquote><h2 id="基本查询"><a href="#基本查询" class="headerlink" title="基本查询"></a>基本查询</h2><p>基本查询也分两种，一种是spring data jpa默认实现，一种是根据查询的方法名来自动解析成SQL。</p><h3 id="预先生成方法"><a href="#预先生成方法" class="headerlink" title="预先生成方法"></a>预先生成方法</h3><p>spring data jpa 默认预先生成了一些基本的CURD的方法，例如：增、删、改等等</p><ol><li><p>继承JpaRepository</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">class</span> <span class="title">UserRepository</span> <span class="keyword">extends</span> <span class="title">JpaRepository</span>&lt;<span class="title">User</span>, <span class="title">Long</span>&gt; </span>&#123;</span><br><span class="line"></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><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">@AutoWired</span></span><br><span class="line"><span class="keyword">private</span> UserRepository userRepository;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testBaseOperation</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">  User user = <span class="keyword">new</span> User();</span><br><span class="line">  userRepository.findAll();</span><br><span class="line">  userRepository.findOne(<span class="number">11</span>);</span><br><span class="line">  userRepository.save(user);</span><br><span class="line">  userRepository.delete(user);</span><br><span class="line">  userRepository.count();</span><br><span class="line">  userRepository.exists(<span class="number">11</span>);</span><br><span class="line">  </span><br><span class="line">  <span class="comment">//...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ol><h3 id="自定义简单查询"><a href="#自定义简单查询" class="headerlink" title="自定义简单查询"></a>自定义简单查询</h3><p>自定义的简单查询就是根据方法名来自动生成SQL，主要的语法是<code>findXXBy</code>,<code>readXXBy</code>,<code>queryXXBy</code>,<code>countXXBy</code>,<code>getXXBy</code>后面跟属性名称。<br></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="function">User <span class="title">findByUserName</span><span class="params">(String userName)</span></span>;</span><br></pre></td></tr></table></figure><br>也可以使用一些关键字<code>And</code>、<code>Or</code><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="function">User <span class="title">findByUserNameOrEmail</span><span class="params">(String userName, String email)</span></span>;</span><br></pre></td></tr></table></figure><br>修改、删除、统计同样是类似语法<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"><span class="function"><span class="keyword">void</span> <span class="title">deleteById</span><span class="params">(Long id)</span></span>;</span><br><span class="line"><span class="function">Long <span class="title">countByUserName</span><span class="params">(String userName)</span></span>;</span><br></pre></td></tr></table></figure><br>基本上SQL体系中的关键词都可以使用，例如：<code>Like</code>、<code>IgnoreCase</code>、<code>OrderBy</code><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></pre></td><td class="code"><pre><span class="line"><span class="function">List&lt;User&gt; <span class="title">findByEmailLike</span><span class="params">(String email)</span></span>;</span><br><span class="line"><span class="function">User <span class="title">findByUserNameIgnoreCase</span><span class="params">(String userName)</span></span>;</span><br><span class="line"><span class="function">List&lt;User&gt; <span class="title">findByUserNameOrderByEmailDesc</span><span class="params">(String email)</span></span>;</span><br></pre></td></tr></table></figure><br>具体的关键字，使用方法和生成的SQL如下表所示<p></p><div class="table-container"><table><thead><tr><th style="text-align:left">Keyword</th><th style="text-align:left">Sample</th><th style="text-align:left">JPQL snippet</th></tr></thead><tbody><tr><td style="text-align:left">And</td><td style="text-align:left">findByLastNameAndFirstName</td><td style="text-align:left">… where x.lastname = ?1 and x.firstname = ?2</td></tr><tr><td style="text-align:left">Or</td><td style="text-align:left">findByLastNameOrFirstName</td><td style="text-align:left">… where x.lastname = ?1 or x.firstname = ?2</td></tr><tr><td style="text-align:left">Is<br>Equals</td><td style="text-align:left">findByFirstNameIs<br>findByFirstNameEquals</td><td style="text-align:left">… where x.firstname = ?1</td></tr><tr><td style="text-align:left">Between</td><td style="text-align:left">findByStartDateBetween</td><td style="text-align:left">… where x.startdate between ?1 and ?2</td></tr><tr><td style="text-align:left">LessThan</td><td style="text-align:left">findByAgeLessThan</td><td style="text-align:left">… where x.age &lt; ?1</td></tr><tr><td style="text-align:left">LessThanEqual</td><td style="text-align:left">findByAgeLessThanEqual</td><td style="text-align:left">… where x.age &lt;= ?1</td></tr><tr><td style="text-align:left">GreaterThan</td><td style="text-align:left">findByAgeGreaterThan</td><td style="text-align:left">… where x.age &gt; ?1</td></tr><tr><td style="text-align:left">GreaterThanEqual</td><td style="text-align:left">findByAgeGreaterThanEqual</td><td style="text-align:left">… where x.age &gt;= ?1</td></tr><tr><td style="text-align:left">After</td><td style="text-align:left">findByStartDateAfter</td><td style="text-align:left">… where x.startdate &gt; ?1</td></tr><tr><td style="text-align:left">Before</td><td style="text-align:left">findByStartDateBefore</td><td style="text-align:left">… where x.startdate &lt; ?1</td></tr><tr><td style="text-align:left">IsNull</td><td style="text-align:left">findByAgeIsNull</td><td style="text-align:left">… where x.age is null</td></tr><tr><td style="text-align:left">IsNotNull<br>NotNull</td><td style="text-align:left">findByAge(Is)NotNull</td><td style="text-align:left">… where x.age not null</td></tr><tr><td style="text-align:left">Like</td><td style="text-align:left">findByFirstNameLike</td><td style="text-align:left">… where x.firstname like ?1</td></tr><tr><td style="text-align:left">NotLike</td><td style="text-align:left">findByFirstNameNotLike</td><td style="text-align:left">… where x.firstName not like ?1</td></tr><tr><td style="text-align:left">StartingWith</td><td style="text-align:left">findByFirstNameStartingWith</td><td style="text-align:left">… where x.firstname like ?1(parameter bound with appended %)</td></tr><tr><td style="text-align:left">EndingWith</td><td style="text-align:left">findByFirstNameEndingWith</td><td style="text-align:left">… where x.firstname like ?1(parameter bound with prepend %)</td></tr><tr><td style="text-align:left">Containing</td><td style="text-align:left">findByFirstNameContaining</td><td style="text-align:left">… where x.firstname like ?1(parameter bound wrapped in %)</td></tr><tr><td style="text-align:left">OrderBy</td><td style="text-align:left">findByAgeOrderByLastNameDesc</td><td style="text-align:left">… where x.age = ?1 order by x.lastname desc</td></tr><tr><td style="text-align:left">Not</td><td style="text-align:left">findByLastNameNot</td><td style="text-align:left">… where x.lastname &lt;&gt; ?1</td></tr><tr><td style="text-align:left">In</td><td style="text-align:left">findAgeIn(Collection ages)</td><td style="text-align:left">… where x.age in ?1</td></tr><tr><td style="text-align:left">NotIn</td><td style="text-align:left">findAgeNotIn(Collection ages)</td><td style="text-align:left">… where x.age not in ?1</td></tr><tr><td style="text-align:left">True</td><td style="text-align:left">findByActiveTrue</td><td style="text-align:left">… where x.active = true</td></tr><tr><td style="text-align:left">False</td><td style="text-align:left">findByActiveFalse</td><td style="text-align:left">… where x.active = false</td></tr><tr><td style="text-align:left">IgnoreCase</td><td style="text-align:left">findByFirstNameIgnoreCase</td><td style="text-align:left">… where UPPER(x.firstname) = UPPER(?1)</td></tr></tbody></table></div><h2 id="复杂查询"><a href="#复杂查询" class="headerlink" title="复杂查询"></a>复杂查询</h2><p>在实际开发中我们需要用到分页、删选、连表等查询的时候就需要特殊的方法或者自定义SQL</p><h3 id="分页查询"><a href="#分页查询" class="headerlink" title="分页查询"></a>分页查询</h3><p>分页查询在实际开发中已经非常普遍了，spring data jpa 已经帮我们实现了分页的功能，在查询方法中，需要传入参数<code>Pageable</code>,当查询中有多个参数时，<code>Pageable</code>建议做为最后一个参数传入<br></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">Page&lt;User&gt; <span class="title">findAll</span><span class="params">(Pageable pageable)</span></span>;</span><br><span class="line"><span class="function">Page&lt;User&gt; <span class="title">findByUserName</span><span class="params">(String userName, Pageable pageable)</span></span>;</span><br></pre></td></tr></table></figure><br><code>Pageable</code>是spring封装的分页实现类，使用的使用需要传入页码，每页条数和排序规则<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></pre></td><td class="code"><pre><span class="line"><span class="meta">@AutoWired</span></span><br><span class="line"><span class="keyword">private</span> UserRepository userRepository;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testPageQuery</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">  <span class="keyword">int</span> pageIndex = <span class="number">1</span>, pageSize = <span class="number">10</span>;</span><br><span class="line">  Sort sort = <span class="keyword">new</span> Sort(Direction.DESC, <span class="string">"id"</span>);</span><br><span class="line">  Pageable pageable = <span class="keyword">new</span> PageRequest(pageIndex, pageSize, sort);</span><br><span class="line">  userRepository.findAll(pageable);</span><br><span class="line">  userRepository.findByUserName(<span class="string">"test"</span>, pageable);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><h3 id="Limit查询"><a href="#Limit查询" class="headerlink" title="Limit查询"></a>Limit查询</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></pre></td><td class="code"><pre><span class="line"><span class="function">User <span class="title">findFirstByOrderByLastnameAsc</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function">User <span class="title">findTopByOrderByAgeDesc</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function">Page&lt;User&gt; <span class="title">queryFirst10ByLastname</span><span class="params">(String lastname, Pageable pageable)</span></span>;</span><br><span class="line"><span class="function">List&lt;User&gt; <span class="title">findFirst10ByLastname</span><span class="params">(String lastname, Sort sort)</span></span>;</span><br><span class="line"><span class="function">List&lt;User&gt; <span class="title">findTop10ByLastname</span><span class="params">(String lastname, Pageable pageable)</span></span>;</span><br></pre></td></tr></table></figure><h3 id="自定义SQL查询"><a href="#自定义SQL查询" class="headerlink" title="自定义SQL查询"></a>自定义SQL查询</h3><p>其实spring data jpa绝大部分的SQL都可以根据方法名定义的方式来实现，但是由于某些原因我们想使用自定义的SQL来查询，spring data jpa也是完美支持的；在SQL的查询方法上面使用<code>@Query</code>注解，如涉及到删除和修改则需要加上<code>@Modifying</code>，也可以根据需要添加 <code>@Transactional</code> 对事务的支持，查询超时的设置等<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><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Modifying</span></span><br><span class="line"><span class="meta">@Query</span>(<span class="string">"update User u set u.username = ?1 where u.id = ?2"</span>)</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">modifyUserNameByUserId</span><span class="params">(String userName,Long id)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Transactional</span></span><br><span class="line"><span class="meta">@Modifying</span></span><br><span class="line"><span class="meta">@Query</span>(<span class="string">"delete from User where id = ?1"</span>)</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">deleteByUserId</span><span class="params">(Long id)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Transactional</span>(timeout = <span class="number">10</span>)</span><br><span class="line"><span class="meta">@Query</span>(<span class="string">"select u from User u where u.email = ?1"</span>)</span><br><span class="line"><span class="function">User <span class="title">findByEmail</span><span class="params">(String email)</span></span>;</span><br></pre></td></tr></table></figure><p></p><h3 id="多表查询"><a href="#多表查询" class="headerlink" title="多表查询"></a>多表查询</h3><p>多表查询在spring data jpa中有两种实现方式，第一种是利用hibernate的级联查询来实现，第二种是创建一个结果集接口来接收查询后的结果，这里主要是第二种方式。</p><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"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">HotelSummary</span> </span>&#123;</span><br><span class="line">  <span class="function">City <span class="title">getCity</span><span class="params">()</span></span>;</span><br><span class="line">  <span class="function">String <span class="title">getName</span><span class="params">()</span></span>;</span><br><span class="line">  <span class="function">Double <span class="title">getAverageRating</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">default</span> Integer <span class="title">getAverageRatingRounded</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> getAverageRating() == <span class="keyword">null</span> ? <span class="keyword">null</span> : (<span class="keyword">int</span>) Math.round(getAverageRating());</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><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="meta">@Query</span>(<span class="string">"select h.city as city, h.name as name, avg(r.rating) as averageRating "</span></span><br><span class="line">      - <span class="string">"from Hotel h left outer join h.reviews r where h.city = ?1 group by h"</span>)</span><br><span class="line"><span class="function">Page&lt;HotelSummary&gt; <span class="title">findByCity</span><span class="params">(City city, Pageable pageable)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Query</span>(<span class="string">"select h.name as name, avg(r.rating) as averageRating "</span></span><br><span class="line">      - <span class="string">"from Hotel h left outer join h.reviews r group by h"</span>)</span><br><span class="line"><span class="function">Page&lt;HotelSummary&gt; <span class="title">findByCity</span><span class="params">(Pageable pageable)</span></span>;</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line">Page&lt;HotemlSummary&gt; hotels = hotelRepository.findByCity(<span class="keyword">new</span> PageRequest(<span class="number">0</span>, <span class="number">10</span>, Direction.ASC, <span class="string">"name"</span>));</span><br><span class="line"><span class="keyword">for</span>(HotelSummary summary : hotels)&#123;</span><br><span class="line">  System.out.println(<span class="string">"Name "</span> + summary.getName());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><blockquote><p>在运行中Spring会给接口（HotelSummary）自动生成一个代理类来接收返回结果，代码汇总使用<code>getXX()</code>的形式来获取</p></blockquote><h2 id="多数据源支持"><a href="#多数据源支持" class="headerlink" title="多数据源支持"></a>多数据源支持</h2><h3 id="同源数据库"><a href="#同源数据库" class="headerlink" title="同源数据库"></a>同源数据库</h3><p>日常项目中因为使用的是分布式开发模式，不同的服务有不同的数据源，常常需要在一个项目中使用多个数据源，因此需要配置spring data jpa 以适合对多数据源的使用，一般分以下三步：</p><ol><li>配置多数据源</li><li>不同源的实体类放入不同包路径</li><li>声明不同的包路径下使用不同的数据源、事务支持</li></ol><p>可参考：<a href="https://www.jianshu.com/p/34730e595a8c" target="_blank" rel="external nofollow noopener noreferrer">Spring Boot多数据源配置与使用</a></p><h3 id="异构数据库"><a href="#异构数据库" class="headerlink" title="异构数据库"></a>异构数据库</h3><p>比如我们的项目中，即需要对mysql的支持，也需要对mongodb的支持等。</p><p>实体类声明<code>@Entity</code>关系型数据库支持类型、声明<code>@Document</code>为mongodb支持类型，不同的数据源使用不同的实体就可以了<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><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="class"><span class="keyword">interface</span> <span class="title">PersonRepository</span> <span class="keyword">extends</span> <span class="title">JpaRepository</span>&lt;<span class="title">Person</span>, <span class="title">Long</span>&gt; </span>&#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Entity</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Person</span> </span>&#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">interface</span> <span class="title">UserRepository</span> <span class="keyword">extends</span> <span class="title">MongoRepository</span>&lt;<span class="title">User</span>, <span class="title">Long</span>&gt; </span>&#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Document</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">User</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><br>但是如果Person既使用mysql也使用mongodb呢，也可以做混合使用<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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">interface</span> <span class="title">JpaPersonRepository</span> <span class="keyword">extends</span> <span class="title">JpaRepository</span>&lt;<span class="title">Person</span>, <span class="title">Long</span>&gt; </span>&#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">interface</span> <span class="title">MongoDBPersonRepository</span> <span class="keyword">extends</span> <span class="title">MongoRepository</span>&lt;<span class="title">Person</span>, <span class="title">Long</span>&gt; </span>&#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Entity</span></span><br><span class="line"><span class="meta">@Document</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Person</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><br>也可以通过对不同的包路径进行声明，比如A包路径下使用mysql，B包路径下使用mongoDB<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></pre></td><td class="code"><pre><span class="line"><span class="meta">@EnableJpaRepository</span>(basePackages = <span class="string">"com.example.repositories.jpa"</span>)</span><br><span class="line"><span class="meta">@EnableMongoRepository</span>(basePackages = <span class="string">"com.example.repositories.mongo"</span>)</span><br><span class="line"><span class="class"><span class="keyword">interface</span> <span class="title">Configuration</span> </span>&#123; &#125;</span><br></pre></td></tr></table></figure><p></p><h2 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h2><h3 id="使用枚举"><a href="#使用枚举" class="headerlink" title="使用枚举"></a>使用枚举</h3><p>使用枚举的时候，我们希望数据库中存储的是枚举对应的String类型，而不是枚举的索引值，需要在属性上面添加<code>@Enumerated(EnumType.STRING)</code>注解<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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Enumerated</span>(EnumType.STRING)</span><br><span class="line"><span class="meta">@Column</span>(nullable = <span class="keyword">true</span>)</span><br><span class="line"><span class="keyword">private</span> UserType type;</span><br></pre></td></tr></table></figure><p></p><h3 id="不需要和数据库映射的属性"><a href="#不需要和数据库映射的属性" class="headerlink" title="不需要和数据库映射的属性"></a>不需要和数据库映射的属性</h3><p>正常情况下，我们在实体上加上注解<code>@Entity</code>，就会让实体类的属性和表的列相关联，如果其中某个属性不需要和数据库来进行关联，而只是展示的时候做计算，只需要加上<code>@Transient</code>属性即可。<br></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Transient</span></span><br><span class="line"><span class="keyword">private</span> String userName;</span><br></pre></td></tr></table></figure><p></p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a href="https://ityouknow.gitbooks.io/spring-data-jpa-reference-documentation/content/" target="_blank" rel="external nofollow noopener noreferrer">Spring Data JPA 参考指南 中文版</a></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;/images/spring-springboot-1.jpg&quot; alt=&quot;spring-springboot-thymeleaf&quot;&gt;&lt;/p&gt;&lt;p&gt;JPA(Java Persistence API) 是Sun官方提出的Java持久化规范。它为Java开发人员提供了一种对象/关联映射工具来管理Java应用中的关系数据。它的出现主要是为了简化现有的持久化开发工作和整合ORM技术，结束现在Hibernate、TopLink、JDO等ORM框架各自为营的局面。值得注意的是，JPA是在充分吸收了现有Hibernate、TopLink、JDO等ORM框架的长处发展而来，具有易于使用，伸缩性强等特点。从目前的开发社区的反应上看，JPA受到了极大的支持和赞扬，其中就包括了Spring和EJB3.0的开发团队。&lt;br&gt;
    
    </summary>
    
    
      <category term="SpringBoot" scheme="http://blog.develophelper.com/categories/springboot/"/>
    
    
      <category term="SpringBoot" scheme="http://blog.develophelper.com/tags/springboot/"/>
    
      <category term="Jpa" scheme="http://blog.develophelper.com/tags/jpa/"/>
    
  </entry>
  
  <entry>
    <title>SpringBoot实战（四）Thymeleaf</title>
    <link href="http://blog.develophelper.com/article/spring-springboot-thymeleaf.html"/>
    <id>http://blog.develophelper.com/article/spring-springboot-thymeleaf.html</id>
    <published>2018-09-04T14:27:08.000Z</published>
    <updated>2020-02-15T15:23:10.034Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/images/spring-springboot-1.jpg" alt="spring-springboot-thymeleaf"></p><p>Thymeleaf是新一代的模板引擎，在Spring 4.0中推荐使用Thymeleaf来做前端模板引擎。<br><a id="more"></a><br>之前在<a href="https://snaildev.github.io/article/spring-springboot-web.html" target="_blank" rel="external nofollow noopener noreferrer">springBoot实战（二）Web</a>中简单介绍了下thymeleaf，本篇将更加全面详细的介绍thymeleaf的使用。</p><h2 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h2><p>简单说，Thymeleaf是一个跟Velocity、FreeMarker类似的模板引擎，它可以完全替代JSP。相比较其他模板引擎，它有如下三个极其吸引人的特点：</p><ol><li>Thymeleaf 在有网络和无网络的环境下皆可运行，即它可以让前端在浏览器查看页面的静态效果，也可以让开发人员在服务器查看带数据的动态页面效果。这是由于它支持html原型，然后在html标签里增加额外的属性来达到模板+数据的展示方式。浏览器解释html时会忽略未定义的标签属性，所以thymeleaf的模板可以静态的运行；当有数据返回到页面时，thymeleaf标签会动态地替换掉静态内容，使页面动态显示。</li><li>Thymeleaf 开箱即用的特性。它提供标准和spring标准两种方言，可以直接套用模板实现JSTL、OGNL表达式效果，避免每天套模板，改JSTL、改标签的困扰，同时开发人员也可以扩展和创建自定义的方言。</li><li>Thymeleaf 提供spring标准方言和一个与Spring MVC完美继承的可选模块，可以快速实现表单绑定、属性编辑器和国际化等功能。</li></ol><h2 id="表达式"><a href="#表达式" class="headerlink" title="表达式"></a>表达式</h2><p>它们分为四类：</p><ol><li>变量表达式</li><li>选择或星号表达式</li><li>文字国际化表达式</li><li>URL表达式</li></ol><h3 id="变量表达式"><a href="#变量表达式" class="headerlink" title="变量表达式"></a>变量表达式</h3><p>变量表达式即OGNL表达式或Spring EL表达式（在Spring 术语中也叫做model attributes）。如下所示：<br></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$&#123;session.user.name&#125;</span><br></pre></td></tr></table></figure><br>它们将以HTML标签的一个属性来表示：<br><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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">li</span> <span class="attr">th:each</span>=<span class="string">"book : $&#123;books&#125;"</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">span</span> <span class="attr">th:text</span>=<span class="string">"$&#123;book.author.name&#125;"</span>&gt;</span>author's name<span class="tag">&lt;/<span class="name">span</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">li</span>&gt;</span></span><br></pre></td></tr></table></figure><p></p><h3 id="选择（星号）表达式"><a href="#选择（星号）表达式" class="headerlink" title="选择（星号）表达式"></a>选择（星号）表达式</h3><p>选择表达式很像变量表达式，不过它们用一个预先选择的对象来代替上下文变量容器（map）来执行，如下：<br></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">*&#123;customer.name&#125;</span><br></pre></td></tr></table></figure><br>被指定的object由<code>th:object</code>属性定义：<br><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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">th:object</span>=<span class="string">"$&#123;book&#125;"</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">span</span> <span class="attr">th:text</span>=<span class="string">"*&#123;title&#125;"</span>&gt;</span>book's title<span class="tag">&lt;<span class="name">span</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure><p></p><h3 id="文字国际化表达式"><a href="#文字国际化表达式" class="headerlink" title="文字国际化表达式"></a>文字国际化表达式</h3><p>文字国际化表达式允许我们从一个外部文件获取区域文字信息（.properties），用Key索引Value，还可以提供一组参数（可选）。<br></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">#&#123;main.title&#125;</span><br><span class="line">#&#123;message.entrycreated($&#123;entryId&#125;)&#125;</span><br></pre></td></tr></table></figure><br>可以在模板文件中找到这样的表达式代码：<br><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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">table</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">th</span> <span class="attr">th:text</span>=<span class="string">"#&#123;header.address.city&#125;"</span>&gt;</span>city<span class="tag">&lt;/<span class="name">th</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">th</span> <span class="attr">th:text</span>=<span class="string">"#&#123;header.address.country&#125;"</span>&gt;</span>country<span class="tag">&lt;/<span class="name">th</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">table</span>&gt;</span></span><br></pre></td></tr></table></figure><p></p><h3 id="URL表达式"><a href="#URL表达式" class="headerlink" title="URL表达式"></a>URL表达式</h3><p>URL表达式值指的是把一个有用的上下文或会话信息添加到URL，这个过程经常被叫做URL重写。<br></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">@&#123;&#x2F;order&#x2F;list&#125;</span><br></pre></td></tr></table></figure><br>URL还可以设置参数<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">@&#123;&#x2F;order&#x2F;details(id&#x3D;$&#123;orderId&#125;)&#125;</span><br></pre></td></tr></table></figure><br>相对路径<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">@&#123;..&#x2F;documents&#x2F;report&#125;</span><br></pre></td></tr></table></figure><br>让我们来看这些表达式：<br><figure class="highlight html"><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="tag">&lt;<span class="name">form</span> <span class="attr">th:action</span>=<span class="string">"@&#123;/createOrder&#125;"</span>&gt;</span>...<span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">"main.html"</span> <span class="attr">th:href</span>=<span class="string">"@&#123;/main&#125;"</span>&gt;</span>...<span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br></pre></td></tr></table></figure><p></p><h3 id="变量表达式VS选择表达式"><a href="#变量表达式VS选择表达式" class="headerlink" title="变量表达式VS选择表达式"></a>变量表达式VS选择表达式</h3><p>如果不考虑上下文的情况下，两者没有区别；星号语法在选定对象上表达，而不是整个上下文。<br>什么是选定对象？就是父标签的值，如下：<br></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">div</span> <span class="attr">th:object</span>=<span class="string">"$&#123;session.user&#125;"</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">p</span>&gt;</span>FirstName: <span class="tag">&lt;<span class="name">span</span> <span class="attr">th:text</span>=<span class="string">"*&#123;firstName&#125;"</span>&gt;</span>Sebastian<span class="tag">&lt;/<span class="name">span</span>&gt;</span>.<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">p</span>&gt;</span>LastName: <span class="tag">&lt;<span class="name">span</span> <span class="attr">th:text</span>=<span class="string">"*&#123;lastName&#125;"</span>&gt;</span>Pepper<span class="tag">&lt;/<span class="name">span</span>&gt;</span>.<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">p</span>&gt;</span>Nationnality: <span class="tag">&lt;<span class="name">span</span> <span class="attr">th:text</span>=<span class="string">"*&#123;nationnality&#125;"</span>&gt;</span>Saturn<span class="tag">&lt;/<span class="name">span</span>&gt;</span>.<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure><br>这完全等价于：<br><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">div</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">p</span>&gt;</span>FirstName: <span class="tag">&lt;<span class="name">span</span> <span class="attr">th:text</span>=<span class="string">"$&#123;session.user.firstName&#125;"</span>&gt;</span>Sebastian<span class="tag">&lt;/<span class="name">span</span>&gt;</span>.<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">p</span>&gt;</span>LastName: <span class="tag">&lt;<span class="name">span</span> <span class="attr">th:text</span>=<span class="string">"$&#123;session.user.lastName&#125;"</span>&gt;</span>Pepper<span class="tag">&lt;/<span class="name">span</span>&gt;</span>.<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">p</span>&gt;</span>Nationnality: <span class="tag">&lt;<span class="name">span</span> <span class="attr">th:text</span>=<span class="string">"$&#123;session.user.nationnality&#125;"</span>&gt;</span>Saturn<span class="tag">&lt;/<span class="name">span</span>&gt;</span>.<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure><br>当然，美元符号和星号语法可以混合使用<br><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">div</span> <span class="attr">th:object</span>=<span class="string">"$&#123;session.user&#125;"</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">p</span>&gt;</span>FirstName: <span class="tag">&lt;<span class="name">span</span> <span class="attr">th:text</span>=<span class="string">"*&#123;firstName&#125;"</span>&gt;</span>Sebastian<span class="tag">&lt;/<span class="name">span</span>&gt;</span>.<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">p</span>&gt;</span>LastName: <span class="tag">&lt;<span class="name">span</span> <span class="attr">th:text</span>=<span class="string">"$&#123;session.user.lastName&#125;"</span>&gt;</span>Pepper<span class="tag">&lt;/<span class="name">span</span>&gt;</span>.<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">p</span>&gt;</span>Nationnality: <span class="tag">&lt;<span class="name">span</span> <span class="attr">th:text</span>=<span class="string">"*&#123;nationnality&#125;"</span>&gt;</span>Saturn<span class="tag">&lt;/<span class="name">span</span>&gt;</span>.<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure><p></p><h2 id="表达式支持的语法"><a href="#表达式支持的语法" class="headerlink" title="表达式支持的语法"></a>表达式支持的语法</h2><h3 id="字面（Literals）"><a href="#字面（Literals）" class="headerlink" title="字面（Literals）"></a>字面（Literals）</h3><ul><li>文本文字（Text literals）: <code>&#39;one text&#39;, &#39;Another one!&#39;,…</code></li><li>数字文本（Number literals）: <code>0, 34, 3.0, 12.3,…</code></li><li>布尔文本（Boolean literals）: <code>true, false</code></li><li>空（Null literal）: <code>null</code></li><li>文字标记（Literal tokens）: <code>one, sometext, main,…</code></li></ul><h3 id="文本操作（Text-operations）"><a href="#文本操作（Text-operations）" class="headerlink" title="文本操作（Text operations）"></a>文本操作（Text operations）</h3><ul><li>字符串连接(String concatenation): <code>+</code></li><li>文本替换（Literal substitutions）: <code>|The name is ${name}|</code></li></ul><h3 id="算术运算（Arithmetic-operations）"><a href="#算术运算（Arithmetic-operations）" class="headerlink" title="算术运算（Arithmetic operations）"></a>算术运算（Arithmetic operations）</h3><ul><li>二元运算符（Binary operators）: <code>+, -, *, /, %</code></li><li>减号（单目运算符）Minus sign (unary operator): <code>-</code></li></ul><h3 id="布尔操作（Boolean-operations）"><a href="#布尔操作（Boolean-operations）" class="headerlink" title="布尔操作（Boolean operations）"></a>布尔操作（Boolean operations）</h3><ul><li>二元运算符（Binary operators）:<code>and, or</code></li><li>布尔否定（一元运算符）Boolean negation (unary operator):<code>!, not</code></li></ul><h3 id="比较和等价-Comparisons-and-equality"><a href="#比较和等价-Comparisons-and-equality" class="headerlink" title="比较和等价(Comparisons and equality)"></a>比较和等价(Comparisons and equality)</h3><ul><li>比较（Comparators）: <code>&gt;, &lt;, &gt;=, &lt;= (gt, lt, ge, le)</code></li><li>等值运算符（Equality operators）:<code>==, != (eq, ne)</code></li></ul><h3 id="条件运算符（Conditional-operators）"><a href="#条件运算符（Conditional-operators）" class="headerlink" title="条件运算符（Conditional operators）"></a>条件运算符（Conditional operators）</h3><ul><li>If-then: <code>(if) ? (then)</code></li><li>If-then-else: <code>(if) ? (then) : (else)</code></li><li>Default: <code>(value) ?: (defaultvalue)</code></li></ul><p>所有这些特征可以被组合并嵌套：<br></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#39;User is of type &#39; + ($&#123;user.isAdmin()&#125; ? &#39;Administrator&#39; : ($&#123;user.type&#125; ?: &#39;Unknown&#39;))</span><br></pre></td></tr></table></figure><p></p><h2 id="常用的th标签"><a href="#常用的th标签" class="headerlink" title="常用的th标签"></a>常用的th标签</h2><div class="table-container"><table><thead><tr><th style="text-align:left">关键字</th><th style="text-align:left">功能介绍</th><th style="text-align:left">案例</th></tr></thead><tbody><tr><td style="text-align:left">th:id</td><td style="text-align:left">替换id</td><td style="text-align:left"><code>&lt;input th:id=&quot;&#39;xxx&#39; + ${collect.id}&quot;/&gt;</code></td></tr><tr><td style="text-align:left">th:text</td><td style="text-align:left">文本替换</td><td style="text-align:left"><code>&lt;p th:text=&quot;${collect.description}&quot;&gt;description&lt;/p&gt;</code></td></tr><tr><td style="text-align:left">th:utext</td><td style="text-align:left">支持html的文本替换</td><td style="text-align:left"><code>&lt;p th:utext=&quot;${htmlcontent}&quot;&gt;conten&lt;/p&gt;</code></td></tr><tr><td style="text-align:left">th:object</td><td style="text-align:left">替换对象</td><td style="text-align:left"><code>&lt;div th:object=&quot;${session.user}&quot;&gt;</code></td></tr><tr><td style="text-align:left">th:value</td><td style="text-align:left">属性赋值</td><td style="text-align:left"><code>&lt;input th:value=&quot;${user.name}&quot; /&gt;</code></td></tr><tr><td style="text-align:left">th:with</td><td style="text-align:left">变量赋值运算</td><td style="text-align:left"><code>&lt;div th:with=&quot;isEven=${prodStat.count}%2==0&quot;&gt;&lt;/div&gt;</code></td></tr><tr><td style="text-align:left">th:style</td><td style="text-align:left">设置样式</td><td style="text-align:left"><code>th:style=&quot;&#39;display:&#39; + @{(${sitrue} ? &#39;none&#39; : &#39;inline-block&#39;)} + &#39;&#39;&quot;</code></td></tr><tr><td style="text-align:left">th:onclick</td><td style="text-align:left">点击事件</td><td style="text-align:left"><code>th:onclick=&quot;&#39;getCollect()&#39;&quot;</code></td></tr><tr><td style="text-align:left">th:each</td><td style="text-align:left">属性赋值</td><td style="text-align:left"><code>&lt;tr th:each=&quot;user,userStat:${users}&quot;&gt;</code></td></tr><tr><td style="text-align:left">th:if</td><td style="text-align:left">判断条件</td><td style="text-align:left"><code>&lt;a th:if=&quot;${userId == collect.userId}&quot; &gt;</code></td></tr><tr><td style="text-align:left">th:unless</td><td style="text-align:left">和th:if判断相反</td><td style="text-align:left"><code>&lt;a th:href=&quot;@{/login}&quot; th:unless=${session.user != null}&gt;Login&lt;/a&gt;</code></td></tr><tr><td style="text-align:left">th:href</td><td style="text-align:left">链接地址</td><td style="text-align:left"><code>&lt;a th:href=&quot;@{/login}&quot; th:unless=${session.user != null}&gt;Login&lt;/a&gt; /&gt;</code></td></tr><tr><td style="text-align:left">th:switch</td><td style="text-align:left">多路选择 配合th:case 使用</td><td style="text-align:left"><code>&lt;div th:switch=&quot;${user.role}&quot;&gt;</code></td></tr><tr><td style="text-align:left">th:case</td><td style="text-align:left">th:switch的一个分支</td><td style="text-align:left"><code>&lt;p th:case=&quot;&#39;admin&#39;&quot;&gt;User is an administrator&lt;/p&gt;</code></td></tr><tr><td style="text-align:left">th:fragment</td><td style="text-align:left">布局标签，定义一个代码片段，方便其它地方引用</td><td style="text-align:left"><code>&lt;div th:fragment=&quot;alert&quot;&gt;</code></td></tr><tr><td style="text-align:left">th:include</td><td style="text-align:left">布局标签，替换内容到引入的文件</td><td style="text-align:left"><code>&lt;head th:include=&quot;layout :: htmlhead&quot; th:with=&quot;title=&#39;xx&#39;&quot;&gt;&lt;/head&gt; /&gt;</code></td></tr><tr><td style="text-align:left">th:replace</td><td style="text-align:left">布局标签，替换整个标签到引入的文件</td><td style="text-align:left"><code>&lt;div th:replace=&quot;fragments/header :: title&quot;&gt;&lt;/div&gt;</code></td></tr><tr><td style="text-align:left">th:selected</td><td style="text-align:left">selected选择框 选中</td><td style="text-align:left"><code>th:selected=&quot;(${xxx.id} == ${configObj.dd})&quot;</code></td></tr><tr><td style="text-align:left">th:src</td><td style="text-align:left">图片类地址引入</td><td style="text-align:left"><code>&lt;img class=&quot;img-responsive&quot; alt=&quot;App Logo&quot; th:src=&quot;@{/img/logo.png}&quot; /&gt;</code></td></tr><tr><td style="text-align:left">th:inline</td><td style="text-align:left">定义js脚本可以使用变量</td><td style="text-align:left"><code>&lt;script type=&quot;text/javascript&quot; th:inline=&quot;javascript&quot;&gt;</code></td></tr><tr><td style="text-align:left">th:action</td><td style="text-align:left">表单提交的地址</td><td style="text-align:left"><code>&lt;form action=&quot;subscribe.html&quot; th:action=&quot;@{/subscribe}&quot;&gt;</code></td></tr><tr><td style="text-align:left">th:remove</td><td style="text-align:left">删除某个属性</td><td style="text-align:left"><code>&lt;tr th:remove=&quot;all&quot;&gt; 1.all:删除包含标签和所有的孩子。2.body:不包含标记删除,但删除其所有的孩子。3.tag:包含标记的删除,但不删除它的孩子。4.all-but-first:删除所有包含标签的孩子,除了第一个。5.none:什么也不做。这个值是有用的动态评估。</code></td></tr><tr><td style="text-align:left">th:attr</td><td style="text-align:left">设置标签属性，多个属性可以用逗号分隔</td><td style="text-align:left">比如 <code>th:attr=&quot;src=@{/image/aa.jpg},title=#{logo}&quot;</code>，此标签不太优雅，一般用的比较少。</td></tr></tbody></table></div><p>还有非常多的标签，这里只列出最常用的几个,由于一个标签内可以包含多个th:x属性，其生效的优先级顺序为:<br></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">include,each,if&#x2F;unless&#x2F;switch&#x2F;case,with,attr&#x2F;attrprepend&#x2F;attrappend,value&#x2F;href,src ,etc,text&#x2F;utext,fragment,remove</span><br></pre></td></tr></table></figure><p></p><h2 id="几种常用的使用方式"><a href="#几种常用的使用方式" class="headerlink" title="几种常用的使用方式"></a>几种常用的使用方式</h2><h3 id="1-赋值、字符串拼接"><a href="#1-赋值、字符串拼接" class="headerlink" title="1. 赋值、字符串拼接"></a>1. 赋值、字符串拼接</h3><figure class="highlight html"><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="tag">&lt;<span class="name">p</span>  <span class="attr">th:text</span>=<span class="string">"$&#123;collect.description&#125;"</span>&gt;</span>description<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">span</span> <span class="attr">th:text</span>=<span class="string">"'Welcome to our application, ' + $&#123;user.name&#125; + '!'"</span>&gt;</span></span><br></pre></td></tr></table></figure><p>字符串拼接还有另外一种简洁的写法<br></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">span</span> <span class="attr">th:text</span>=<span class="string">"|Welcome to our application, $&#123;user.name&#125;!|"</span>&gt;</span></span><br></pre></td></tr></table></figure><p></p><h3 id="2-条件判断If-Unless"><a href="#2-条件判断If-Unless" class="headerlink" title="2. 条件判断If/Unless"></a>2. 条件判断If/Unless</h3><p>Thymeleaf中使用<code>th:if</code>和<code>th:unless</code>属性进行条件判断，下面的例子中，<code>&lt;a&gt;</code>标签只有在<code>th:if</code>中条件成立时才显示：<br></p><figure class="highlight html"><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="tag">&lt;<span class="name">a</span> <span class="attr">th:if</span>=<span class="string">"$&#123;myself=='yes'&#125;"</span> &gt;</span>test<span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">a</span> <span class="attr">th:unless</span>=<span class="string">$&#123;session.user</span> != <span class="string">null&#125;</span> <span class="attr">th:href</span>=<span class="string">"@&#123;/login&#125;"</span> &gt;</span>Login<span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br></pre></td></tr></table></figure><br><code>th:unless</code>与<code>th:if</code>恰好相反，只有表达式中的条件不成立，才会显示其内容。<br>也可以使用 <code>(if) ? (then) : (else)</code> 这种语法来判断显示的内容<p></p><h3 id="3-for循环"><a href="#3-for循环" class="headerlink" title="3. for循环"></a>3. for循环</h3><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">tr</span>  <span class="attr">th:each</span>=<span class="string">"collect,iterStat : $&#123;collects&#125;"</span>&gt;</span> </span><br><span class="line">    <span class="tag">&lt;<span class="name">th</span> <span class="attr">scope</span>=<span class="string">"row"</span> <span class="attr">th:text</span>=<span class="string">"$&#123;collect.id&#125;"</span>&gt;</span>1<span class="tag">&lt;/<span class="name">th</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">td</span> &gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">img</span> <span class="attr">th:src</span>=<span class="string">"$&#123;collect.webLogo&#125;"</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">td</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">td</span> <span class="attr">th:text</span>=<span class="string">"$&#123;collect.url&#125;"</span>&gt;</span>Mark<span class="tag">&lt;/<span class="name">td</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">td</span> <span class="attr">th:text</span>=<span class="string">"$&#123;collect.title&#125;"</span>&gt;</span>Otto<span class="tag">&lt;/<span class="name">td</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">td</span> <span class="attr">th:text</span>=<span class="string">"$&#123;collect.description&#125;"</span>&gt;</span>@mdo<span class="tag">&lt;/<span class="name">td</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">td</span> <span class="attr">th:text</span>=<span class="string">"$&#123;iterStat.index&#125;"</span>&gt;</span>index<span class="tag">&lt;/<span class="name">td</span>&gt;</span></span><br><span class="line"> <span class="tag">&lt;/<span class="name">tr</span>&gt;</span></span><br></pre></td></tr></table></figure><p>iterStat称作状态变量，属性有：</p><ul><li>index:当前迭代对象的index（从0开始计算）</li><li>count: 当前迭代对象的index(从1开始计算)</li><li>size:被迭代对象的大小</li><li>current:当前迭代变量</li><li>even/odd:布尔值，当前循环是否是偶数/奇数（从0开始计算）</li><li>first:布尔值，当前循环是否是第一个</li><li>last:布尔值，当前循环是否是最后一个</li></ul><h3 id="4-URL"><a href="#4-URL" class="headerlink" title="4. URL"></a>4. URL</h3><p>URL在Web应用模板中占据着十分重要的地位，需要特别注意的是Thymeleaf对于URL的处理是通过语法<strong>@{…}</strong>来处理的。<br>如果需要Thymeleaf对URL进行渲染，那么务必使用<code>th:href</code>，<code>th:src</code>等属性，下面是一个例子<br></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="comment">&lt;!-- Will produce 'http://localhost:8080/standard/unread' (plus rewriting) --&gt;</span></span><br><span class="line"> <span class="tag">&lt;<span class="name">a</span>  <span class="attr">th:href</span>=<span class="string">"@&#123;/standard/&#123;type&#125;(type=$&#123;type&#125;)&#125;"</span>&gt;</span>view<span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- Will produce '/gtvg/order/3/details' (plus rewriting) --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">"details.html"</span> <span class="attr">th:href</span>=<span class="string">"@&#123;/order/&#123;orderId&#125;/details(orderId=$&#123;o.id&#125;)&#125;"</span>&gt;</span>view<span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br></pre></td></tr></table></figure><br>设置背景<br><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:style</span>=<span class="string">"'background:url(' + @&#123;/&lt;path-to-image&gt;&#125; + ');'"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure><br>根据属性值改变背景<br><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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"media-object resource-card-image"</span>  </span></span><br><span class="line"><span class="tag">    <span class="attr">th:style</span>=<span class="string">"'background:url(' + @&#123;($&#123;collect.webLogo&#125;=='' ? 'img/favicon.png' : $&#123;collect.webLogo&#125;)&#125; + ')'"</span> &gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure><br>几点说明：<p></p><ul><li>上例中URL最后的<code>(orderId=${o.id})</code> 表示将括号内的内容作为URL参数处理，该语法避免使用字符串拼接，大大提高了可读性</li><li><code>@{...}</code>表达式中可以通过<code>{orderId}</code>访问Context中的orderId变量</li><li><code>@{/order}</code>是Context相关的相对路径，在渲染时会自动添加上当前Web应用的Context名字，假设context名字为app，那么结果应该是/app/order</li></ul><h3 id="5-内联js"><a href="#5-内联js" class="headerlink" title="5. 内联js"></a>5. 内联js</h3><p>内联文本：<code>[[...]]</code>内联文本的表示方式，使用时，必须先用<code>th:inline=&quot;text/javascript/none&quot;</code>激活，<code>th:inline</code>可以在父级标签内使用，甚至作为body的标签。内联文本尽管比<code>th:text</code>的代码少，不利于原型显示。<br></p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">th:inline</span>=<span class="string">"javascript"</span>&gt;</span></span><br><span class="line"><span class="actionscript"><span class="comment">/*&lt;![CDATA[*/</span></span></span><br><span class="line">...</span><br><span class="line"><span class="actionscript"><span class="keyword">var</span> username = <span class="comment">/*[[$&#123;sesion.user.name&#125;]]*/</span> <span class="string">'Sebastian'</span>;</span></span><br><span class="line"><span class="actionscript"><span class="keyword">var</span> size = <span class="comment">/*[[$&#123;size&#125;]]*/</span> <span class="number">0</span>;</span></span><br><span class="line">...</span><br><span class="line"><span class="actionscript"><span class="comment">/*]]&gt;*/</span></span></span><br><span class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure><br>js附加代码：<br><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></pre></td><td class="code"><pre><span class="line">/*[+</span><br><span class="line">var msg = 'This is a working application';</span><br><span class="line">+]*/</span><br></pre></td></tr></table></figure><br>js移除代码：<br><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></pre></td><td class="code"><pre><span class="line">/*[- */</span><br><span class="line">var msg = 'This is a non-working template';</span><br><span class="line">/* -]*/</span><br></pre></td></tr></table></figure><p></p><h3 id="6-内嵌变量"><a href="#6-内嵌变量" class="headerlink" title="6. 内嵌变量"></a>6. 内嵌变量</h3><p>为了模板更加易用，Thymeleaf还提供了一系列Utility对象（内置于Context中），可以通过#直接访问：</p><ul><li>dates ： java.util.Date的功能方法类。</li><li>calendars : 类似#dates，面向java.util.Calendar</li><li>numbers : 格式化数字的功能方法类</li><li>strings : 字符串对象的功能类，contains,startWiths,prepending/appending等等。</li><li>objects: 对objects的功能类操作。</li><li>bools: 对布尔值求值的功能方法。</li><li>arrays：对数组的功能类方法。</li><li>lists: 对lists功能类方法</li><li>sets</li><li>maps</li><li>…<br>下面用一段代码来举例一些常用的方法：</li></ul><p><strong>dates</strong><br></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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">&#x2F;*</span><br><span class="line"> * Format date with the specified pattern</span><br><span class="line"> * Also works with arrays, lists or sets</span><br><span class="line"> *&#x2F;</span><br><span class="line">$&#123;#dates.format(date, &#39;dd&#x2F;MMM&#x2F;yyyy HH:mm&#39;)&#125;</span><br><span class="line">$&#123;#dates.arrayFormat(datesArray, &#39;dd&#x2F;MMM&#x2F;yyyy HH:mm&#39;)&#125;</span><br><span class="line">$&#123;#dates.listFormat(datesList, &#39;dd&#x2F;MMM&#x2F;yyyy HH:mm&#39;)&#125;</span><br><span class="line">$&#123;#dates.setFormat(datesSet, &#39;dd&#x2F;MMM&#x2F;yyyy HH:mm&#39;)&#125;</span><br><span class="line"></span><br><span class="line">&#x2F;*</span><br><span class="line"> * Create a date (java.util.Date) object for the current date and time</span><br><span class="line"> *&#x2F;</span><br><span class="line">$&#123;#dates.createNow()&#125;</span><br><span class="line"></span><br><span class="line">&#x2F;*</span><br><span class="line"> * Create a date (java.util.Date) object for the current date (time set to 00:00)</span><br><span class="line"> *&#x2F;</span><br><span class="line">$&#123;#dates.createToday()&#125;</span><br></pre></td></tr></table></figure><p></p><p><strong>strings</strong><br></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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">&#x2F;*</span><br><span class="line"> * Check whether a String is empty (or null). Performs a trim() operation before check</span><br><span class="line"> * Also works with arrays, lists or sets</span><br><span class="line"> *&#x2F;</span><br><span class="line">$&#123;#strings.isEmpty(name)&#125;</span><br><span class="line">$&#123;#strings.arrayIsEmpty(nameArr)&#125;</span><br><span class="line">$&#123;#strings.listIsEmpty(nameList)&#125;</span><br><span class="line">$&#123;#strings.setIsEmpty(nameSet)&#125;</span><br><span class="line"></span><br><span class="line">&#x2F;*</span><br><span class="line"> * Check whether a String starts or ends with a fragment</span><br><span class="line"> * Also works with arrays, lists or sets</span><br><span class="line"> *&#x2F;</span><br><span class="line">$&#123;#strings.startsWith(name,&#39;Don&#39;)&#125;                  &#x2F;&#x2F; also array*, list* and set*</span><br><span class="line">$&#123;#strings.endsWith(name,endingFragment)&#125;           &#x2F;&#x2F; also array*, list* and set*</span><br><span class="line"></span><br><span class="line">&#x2F;*</span><br><span class="line"> * Compute length</span><br><span class="line"> * Also works with arrays, lists or sets</span><br><span class="line"> *&#x2F;</span><br><span class="line">$&#123;#strings.length(str)&#125;</span><br><span class="line"></span><br><span class="line">&#x2F;*</span><br><span class="line"> * Null-safe comparison and concatenation</span><br><span class="line"> *&#x2F;</span><br><span class="line">$&#123;#strings.equals(str)&#125;</span><br><span class="line">$&#123;#strings.equalsIgnoreCase(str)&#125;</span><br><span class="line">$&#123;#strings.concat(str)&#125;</span><br><span class="line">$&#123;#strings.concatReplaceNulls(str)&#125;</span><br><span class="line"></span><br><span class="line">&#x2F;*</span><br><span class="line"> * Random</span><br><span class="line"> *&#x2F;</span><br><span class="line">$&#123;#strings.randomAlphanumeric(count)&#125;</span><br></pre></td></tr></table></figure><p></p><h2 id="布局"><a href="#布局" class="headerlink" title="布局"></a>布局</h2><p>使用thymeleaf布局非常的方便</p><p>定义代码片段<br></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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">footer</span> <span class="attr">th:fragment</span>=<span class="string">"copy"</span>&gt;</span> </span><br><span class="line">  <span class="symbol">&amp;copy;</span> 2016</span><br><span class="line"><span class="tag">&lt;/<span class="name">footer</span>&gt;</span></span><br></pre></td></tr></table></figure><br>在页面任何地方引入：<br><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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span> </span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span> <span class="attr">th:include</span>=<span class="string">"footer :: copy"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span> <span class="attr">th:replace</span>=<span class="string">"footer :: copy"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br></pre></td></tr></table></figure><br><code>th:include</code> 和 <code>th:replace</code>区别，include只是加载，replace是替换<p></p><p>返回的HTML如下：<br></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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span> </span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span>&gt;</span> <span class="symbol">&amp;copy;</span> 2016 <span class="tag">&lt;/<span class="name">div</span>&gt;</span> </span><br><span class="line">  <span class="tag">&lt;<span class="name">footer</span>&gt;</span> <span class="symbol">&amp;copy;</span> 2016 <span class="tag">&lt;/<span class="name">footer</span>&gt;</span> </span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br></pre></td></tr></table></figure><br>下面是一个常用的后台页面布局，将整个页面分为头部，尾部、菜单栏、隐藏栏，点击菜单只改变content区域的页面<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">body</span> <span class="attr">class</span>=<span class="string">"layout-fixed"</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span> <span class="attr">th:fragment</span>=<span class="string">"navbar"</span>  <span class="attr">class</span>=<span class="string">"wrapper"</span>  <span class="attr">role</span>=<span class="string">"navigation"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">th:replace</span>=<span class="string">"fragments/header :: header"</span>&gt;</span>Header<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">th:replace</span>=<span class="string">"fragments/left :: left"</span>&gt;</span>left<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">th:replace</span>=<span class="string">"fragments/sidebar :: sidebar"</span>&gt;</span>sidebar<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">layout:fragment</span>=<span class="string">"content"</span> <span class="attr">id</span>=<span class="string">"content"</span> &gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">th:replace</span>=<span class="string">"fragments/footer :: footer"</span>&gt;</span>footer<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br></pre></td></tr></table></figure><br>任何页面想使用这样的布局值只需要替换中间的 content模块即可<br><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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">html</span> <span class="attr">xmlns:th</span>=<span class="string">"http://www.thymeleaf.org"</span> <span class="attr">layout:decorator</span>=<span class="string">"layout"</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">section</span> <span class="attr">layout:fragment</span>=<span class="string">"content"</span>&gt;</span></span><br><span class="line">   ...</span><br></pre></td></tr></table></figure><br>也可以在引用模版的时候传参<br><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">head</span> <span class="attr">th:include</span>=<span class="string">"layout :: htmlhead"</span> <span class="attr">th:with</span>=<span class="string">"title='Hello'"</span>&gt;</span><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br></pre></td></tr></table></figure><br>layout 是文件地址，如果有文件夹可以这样写 <code>fileName/layout:htmlhead</code><p></p><p>htmlhead 是指定义的代码片段 如 <code>th:fragment=&quot;copy&quot;</code></p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://www.cnblogs.com/ityouknow/p/5833560.html" target="_blank" rel="noopener">Thymeleaf使用详解</a></li><li><a href="https://blog.csdn.net/column/details/16513.html" target="_blank" rel="external nofollow noopener noreferrer">Thymeleaf学习总结</a></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;/images/spring-springboot-1.jpg&quot; alt=&quot;spring-springboot-thymeleaf&quot;&gt;&lt;/p&gt;&lt;p&gt;Thymeleaf是新一代的模板引擎，在Spring 4.0中推荐使用Thymeleaf来做前端模板引擎。&lt;br&gt;
    
    </summary>
    
    
      <category term="SpringBoot" scheme="http://blog.develophelper.com/categories/springboot/"/>
    
    
      <category term="SpringBoot" scheme="http://blog.develophelper.com/tags/springboot/"/>
    
      <category term="Thymeleaf" scheme="http://blog.develophelper.com/tags/thymeleaf/"/>
    
  </entry>
  
  <entry>
    <title>SpringBoot实战（三）Redis</title>
    <link href="http://blog.develophelper.com/article/spring-springboot-redis.html"/>
    <id>http://blog.develophelper.com/article/spring-springboot-redis.html</id>
    <published>2018-09-03T12:32:28.000Z</published>
    <updated>2020-02-15T15:23:10.033Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/images/spring-springboot-1.jpg" alt="spring-springboot-redis"></p><p>Spring Boot 对常用的数据库支持外，对nosql数据库也进行了封装自动化，<br><a id="more"></a></p><h2 id="Redis介绍"><a href="#Redis介绍" class="headerlink" title="Redis介绍"></a>Redis介绍</h2><p>Redis是目前业界使用最广泛的内存数据存储。相比Memcached，Redis支持更丰富的数据结构，例如hashes,lists,sets等，同时支持数据的持久化。除此之外，Redis还提供一些类数据库的特性，比如事务，HA，主从库等。可以说Redis兼具了缓存系统和数据库的一些特性，因此有着丰富的应用场景。本文介绍Redis在Spring Boot中两个典型的应用场景。</p><h2 id="如何使用"><a href="#如何使用" class="headerlink" title="如何使用"></a>如何使用</h2><ol><li><p>引入spring-boot-starter-data-redis</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-data-redis<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure></li><li><p>添加redis配置</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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">#Redis数据库索引（默认为0）</span><br><span class="line">spring.redis.database&#x3D;0</span><br><span class="line">#Redis服务器地址</span><br><span class="line">spring.redis.host&#x3D;127.0.0.1</span><br><span class="line">#Redis服务器连接端口</span><br><span class="line">spring.redis.port&#x3D;6379</span><br><span class="line">#Redis服务器连接密码（默认为空）</span><br><span class="line">spring.redis.password&#x3D;</span><br><span class="line">#Redis连接池最大连接数（使用负值表示没有限制）</span><br><span class="line">spring.redis.pool.max-active&#x3D;8</span><br><span class="line">#Redis连接池最大阻塞等待时间（使用负值表示没有限制）</span><br><span class="line">spring.redis.pool.max-wait&#x3D;-1</span><br><span class="line">#Redis连接池中的最大空闲连接</span><br><span class="line">spring.redis.pool.max-idle&#x3D;8</span><br><span class="line">#Redis连接池中的最小空闲连接</span><br><span class="line">spring.redis.pool.min-idle&#x3D;0</span><br><span class="line">#Redis连接超时时间（毫秒）</span><br><span class="line">spring.redis.timeout&#x3D;0</span><br></pre></td></tr></table></figure></li><li><p>添加cache的配置类</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@EnableCaching</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">RedisConfig</span> <span class="keyword">extends</span> <span class="title">CachingConfigurerSupport</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="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> KeyGenerator <span class="title">keyGenerator</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> KeyGenerator() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="function"><span class="keyword">public</span> Object <span class="title">generate</span><span class="params">(Object o, Method method, Object... objects)</span> </span>&#123;</span><br><span class="line">                StringBuilder sb = <span class="keyword">new</span> StringBuilder();</span><br><span class="line">                sb.append(o.getClass().getName());</span><br><span class="line">                sb.append(method.getName());</span><br><span class="line">                <span class="keyword">for</span> (Object obj : objects) &#123;</span><br><span class="line">                    sb.append(obj.toString());</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">return</span> sb.toString();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="meta">@SuppressWarnings</span>(<span class="string">"rawtypes"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> CacheManager <span class="title">cacheManager</span><span class="params">(RedisTemplate redisTemplate)</span> </span>&#123;</span><br><span class="line">        RedisCacheManager rcm = <span class="keyword">new</span> RedisCacheManager(redisTemplate);</span><br><span class="line">        <span class="comment">// 设置缓存过期时间</span></span><br><span class="line">        <span class="comment">// rcm.setDefaultExpiration(60); //秒</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> rcm;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> RedisTemplate&lt;String, String&gt; <span class="title">redisTemplate</span><span class="params">(RedisConnectionFactory factory)</span> </span>&#123;</span><br><span class="line">        StringRedisTemplate template = <span class="keyword">new</span> StringRedisTemplate(factory);</span><br><span class="line">        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = <span class="keyword">new</span> Jackson2JsonRedisSerializer(Object<span class="class">.<span class="keyword">class</span>)</span>;</span><br><span class="line">        ObjectMapper om = <span class="keyword">new</span> ObjectMapper();</span><br><span class="line">        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);</span><br><span class="line">        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);</span><br><span class="line">        jackson2JsonRedisSerializer.setObjectMapper(om);</span><br><span class="line">        template.setValueSerializer(jackson2JsonRedisSerializer);</span><br><span class="line">        template.afterPropertiesSet();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> template;</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><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@RunWith</span>(SpringJUnit4ClassRunner<span class="class">.<span class="keyword">class</span>)</span></span><br><span class="line"><span class="class">@<span class="title">SpringBootTest</span></span></span><br><span class="line"><span class="class"><span class="title">public</span> <span class="title">class</span> <span class="title">RedisTests</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> StringRedisTemplate stringRedisTemplate;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> RedisTemplate redisTemplate;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">test</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        stringRedisTemplate.opsForValue().set(<span class="string">"aaa"</span>, <span class="string">"111"</span>);</span><br><span class="line">        Assert.assertEquals(<span class="string">"111"</span>, stringRedisTemplate.opsForValue().get(<span class="string">"aaa"</span>));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testObj</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        User user = <span class="keyword">new</span> User(<span class="string">"aa@gmail.com"</span>, <span class="string">"aa"</span>, <span class="string">"aa123456"</span>);</span><br><span class="line">        ValueOperations&lt;String, User&gt; operations = redisTemplate.opsForValue();</span><br><span class="line">        operations.set(<span class="string">"com.example"</span>, user);</span><br><span class="line">        operations.set(<span class="string">"com.example.timeout"</span>, user, <span class="number">1</span>, TimeUnit.SECONDS);</span><br><span class="line">        Thread.sleep(<span class="number">1000</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">boolean</span> exists = redisTemplate.hasKey(<span class="string">"com.example.timeout"</span>);</span><br><span class="line">        <span class="keyword">if</span> (exists) &#123;</span><br><span class="line">            System.out.println(<span class="string">"exists is true"</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            System.out.println(<span class="string">"exists is false"</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>以上都是手动使用的方式，如何在查找数据库的时候自动使用缓存呢，看下面：</p></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><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">@RestController</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UserController</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> UserRepository userRepository;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@GetMapping</span>(<span class="string">"/getUser"</span>)</span><br><span class="line">    <span class="meta">@Cacheable</span>(value=<span class="string">"user-key"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> User <span class="title">getUser</span><span class="params">()</span></span>&#123;</span><br><span class="line">        User user = userRepository.findUserByUserName(<span class="string">"bb1"</span>);</span><br><span class="line">        System.out.println(<span class="string">"若下面没出现“无缓存的时候调用”字样且能打印出数据表示测试成功"</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> user;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>其中value的值就是就是缓存到redis中的key。</p></li></ol><h2 id="共享Session"><a href="#共享Session" class="headerlink" title="共享Session"></a>共享Session</h2><p>spring boot 整合 spring-session-data-redis<br>分布式系统中，session共享有很多的解决方案，其中托管到缓存中应该是最常用的方案之一。</p><h3 id="Spring-Session官方说明"><a href="#Spring-Session官方说明" class="headerlink" title="Spring Session官方说明"></a>Spring Session官方说明</h3><p>Spring Session provides an API and implementations for managing a user’s session information.</p><h3 id="如何使用-1"><a href="#如何使用-1" class="headerlink" title="如何使用"></a>如何使用</h3><ol><li><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></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--当@EnableRedisHttpSession注解找不到时引入--&gt;</span></span><br><span class="line"><span class="comment">&lt;!--&lt;dependency&gt;--&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--&lt;groupId&gt;org.springframework.session&lt;/groupId&gt;--&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--&lt;artifactId&gt;spring-session&lt;/artifactId&gt;--&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--&lt;version&gt;1.3.3.RELEASE&lt;/version&gt;--&gt;</span></span><br><span class="line"><span class="comment">&lt;!--&lt;/dependency&gt;--&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.session<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-session-data-redis<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure></li><li><p>Session配置</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">@Configuration</span></span><br><span class="line"><span class="meta">@EnableRedisHttpSession</span>(maxInactiveIntervalInSeconds = <span class="number">86400</span>*<span class="number">30</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SessionConfig</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>maxInactiveIntervalInSeconds: 设置Session失效时间。<br>使用Redis Session之后，原Boot的server.session.timeout属性不再生效</p></blockquote></li></ol><p>好了，这样就配置完成了，让我们来测试下</p><ol><li>测试<br>添加测试方法获取sessionId<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@GetMapping</span>(<span class="string">"/uid"</span>)</span><br><span class="line"><span class="function"><span class="keyword">public</span> String <span class="title">uid</span><span class="params">(HttpSession session)</span> </span>&#123;</span><br><span class="line">    UUID uid = (UUID) session.getAttribute(<span class="string">"uid"</span>);</span><br><span class="line">    <span class="keyword">if</span> (uid == <span class="keyword">null</span>) &#123;</span><br><span class="line">        uid = UUID.randomUUID();</span><br><span class="line">    &#125;</span><br><span class="line">    session.setAttribute(<span class="string">"uid"</span>, uid);</span><br><span class="line">    <span class="keyword">return</span> session.getId();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>登录redis输入<code>keys &#39;*session*&#39;</code>进行查询<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1:6379&gt; keys &#39;*session*&#39;</span><br><span class="line">1) &quot;spring:session:sessions:expires:21099c0b-ae12-4ebc-ac8f-7d2f9baf5cdf&quot;</span><br><span class="line">2) &quot;spring:session:expirations:1541400420000&quot;</span><br><span class="line">3) &quot;spring:session:sessions:21099c0b-ae12-4ebc-ac8f-7d2f9baf5cdf&quot;</span><br></pre></td></tr></table></figure>其中 1541400420000为失效时间，意思是这个时间后session失效，<code>21099c0b-ae12-4ebc-ac8f-7d2f9baf5cdf</code> 为sessionId,登录<a href="http://localhost:8080/uid" target="_blank" rel="external nofollow noopener noreferrer">http://localhost:8080/uid</a> 发现会一致，就说明session 已经在redis里面进行有效的管理了。</li></ol><h3 id="如何在两台或者多台服务器中共享session"><a href="#如何在两台或者多台服务器中共享session" class="headerlink" title="如何在两台或者多台服务器中共享session"></a>如何在两台或者多台服务器中共享session</h3><p>其实就是按照上面的步骤在另一个项目中再次配置一次，启动后就自动进行了session共享。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;/images/spring-springboot-1.jpg&quot; alt=&quot;spring-springboot-redis&quot;&gt;&lt;/p&gt;&lt;p&gt;Spring Boot 对常用的数据库支持外，对nosql数据库也进行了封装自动化，&lt;br&gt;
    
    </summary>
    
    
      <category term="SpringBoot" scheme="http://blog.develophelper.com/categories/springboot/"/>
    
    
      <category term="SpringBoot" scheme="http://blog.develophelper.com/tags/springboot/"/>
    
      <category term="Redis" scheme="http://blog.develophelper.com/tags/redis/"/>
    
  </entry>
  
  <entry>
    <title>SpringBoot实战（二）Web</title>
    <link href="http://blog.develophelper.com/article/spring-springboot-web.html"/>
    <id>http://blog.develophelper.com/article/spring-springboot-web.html</id>
    <published>2018-09-02T12:36:41.000Z</published>
    <updated>2020-02-15T15:23:10.034Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/images/spring-springboot-1.jpg" alt="spring-springboot-web"></p><p>Spring Boot Web开发非常的简单，其中包括常用的json输出、filters、property、log等。<br><a id="more"></a></p><h2 id="接口开发"><a href="#接口开发" class="headerlink" title="接口开发"></a>接口开发</h2><p>在以前的spring开发的时候需要我们提供json接口的时候需要做以下配置：</p><ol><li>添加jackon等相关jar包</li><li>配置spring controller扫描</li><li>对接的方法添加@ResponseBody</li></ol><p>就这样我们会经常由于配置问题，导致406错误等等，那么spring boot是如何做的呢，只需要给Controller类添加<code>@RestController</code> 注解即可，那么默认类中的方法都会以json的格式返回。<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><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">HelloWorldController</span> </span>&#123;</span><br><span class="line">    <span class="meta">@GetMapping</span>(<span class="string">"/getUser"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> User <span class="title">getUser</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.setUserName(<span class="string">"snaildev"</span>);</span><br><span class="line">        user.setPassword(<span class="string">"123456"</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><br>如果我们需要使用页面开发只要使用<code>@Controller</code>，下面会结合模板来说明。<p></p><h2 id="自定义Filter"><a href="#自定义Filter" class="headerlink" title="自定义Filter"></a>自定义Filter</h2><p>我们常常在项目中会使用filters用于记录调用日志、排除有XSS威胁的字符、执行权限验证等等。Spring Boot自动添加了OrderedCharacterEncodingFilter和HiddenHttpMethodFilter,并且我们可以自定义Filter。</p><p>两个步骤：</p><ol><li>实现Filter接口，实现Filter方法；</li><li>添加<code>@Configuration</code> 注解，将自定义的Filter加入过滤链；</li></ol><p>ok，直接上代码</p><p>WebConfiguration.java<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><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">WebConfiguration</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> RemoteIpFilter <span class="title">remoteIpFilter</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> RemoteIpFilter();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> FilterRegistrationBean <span class="title">filterRegistrationBean</span><span class="params">()</span></span>&#123;</span><br><span class="line">        FilterRegistrationBean registrationBean = <span class="keyword">new</span> FilterRegistrationBean();</span><br><span class="line">        registrationBean.setFilter(<span class="keyword">new</span> MyFilter());</span><br><span class="line">        registrationBean.addUrlPatterns(<span class="string">"/*"</span>);</span><br><span class="line">        registrationBean.addInitParameter(<span class="string">"paramName"</span>,<span class="string">"paramValue"</span>);</span><br><span class="line">        registrationBean.setName(<span class="string">"MyFilter"</span>);</span><br><span class="line">        registrationBean.setOrder(<span class="number">1</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> registrationBean;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>MyFilter.java<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><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyFilter</span> <span class="keyword">implements</span> <span class="title">Filter</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">init</span><span class="params">(FilterConfig filterConfig)</span> <span class="keyword">throws</span> ServletException </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">@Override</span></span><br><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 servletRequest, ServletResponse servletResponse, FilterChain filterChain)</span> <span class="keyword">throws</span> IOException, ServletException </span>&#123;</span><br><span class="line">        HttpServletRequest request = (HttpServletRequest) servletRequest;</span><br><span class="line">        System.out.println(<span class="string">"this is MyFilter，url："</span> + request.getRequestURI());</span><br><span class="line">        filterChain.doFilter(servletRequest, servletResponse);</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">destroy</span><span class="params">()</span> </span>&#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><p></p><h2 id="自定义Property"><a href="#自定义Property" class="headerlink" title="自定义Property"></a>自定义Property</h2><p>在Web开发中，我们经常需要自定义一些配置文件，如何使用呢?</p><p><strong>配置在application.properties中</strong><br></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">com.example.title&#x3D;SnailDev&#39;s Blog</span><br><span class="line">com.example.description&#x3D;Coding is everything in my life.</span><br></pre></td></tr></table></figure><p></p><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><span class="line">10</span><br></pre></td><td class="code"><pre><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">ExampleProperties</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Value</span>(<span class="string">"$&#123;com.example.title&#125;"</span>)</span><br><span class="line">    <span class="keyword">private</span> String title;</span><br><span class="line">    <span class="meta">@Value</span>(<span class="string">"$&#123;com.example.description&#125;"</span>)</span><br><span class="line">    <span class="keyword">private</span> String description;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//省略getter settet方法</span></span><br><span class="line"></span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p></p><p><strong>log配置</strong><br>配置输出的地址和输出的级别<br></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">logging.path&#x3D;&#x2F;user&#x2F;local&#x2F;log</span><br><span class="line">logging.level.com.example&#x3D;DEBUG</span><br><span class="line">logging.level.org.springframework.web&#x3D;INFO</span><br><span class="line">logging.level.org.hibernate&#x3D;ERROR</span><br></pre></td></tr></table></figure><br>path为本机log地址，logging.level 后面可以根据包路径配置不同资源的log级别<p></p><h2 id="数据库操作"><a href="#数据库操作" class="headerlink" title="数据库操作"></a>数据库操作</h2><p>在这里我们重点来看一下mysql、spring data jpa的使用，其中mysql就不用说了，大家应该都很熟悉，jpa是利用Hibernate生成各种自动化的sql，如果只是简单的增删改查，基本上不用手写了，spring 内部已经帮我们封装实现了。</p><p>下面简单介绍一下如何在spring boot中简单使用</p><h3 id="1-添加相关的jar包依赖"><a href="#1-添加相关的jar包依赖" class="headerlink" title="1. 添加相关的jar包依赖"></a>1. 添加相关的jar包依赖</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-data-jpa<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>mysql<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>mysql-connector-java<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="2-添加配置文件"><a href="#2-添加配置文件" class="headerlink" title="2. 添加配置文件"></a>2. 添加配置文件</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">spring.datasource.url&#x3D;jdbc:mysql:&#x2F;&#x2F;localhost:3306&#x2F;test</span><br><span class="line">spring.datasource.username&#x3D;root</span><br><span class="line">spring.datasource.password&#x3D;123456</span><br><span class="line">spring.datasource.driver-class-name&#x3D;com.mysql.jdbc.Driver</span><br><span class="line"></span><br><span class="line">spring.jpa.properties.hibernate.hbm2ddl.auto&#x3D;update</span><br><span class="line">spring.jpa.properties.hibernate.dialect&#x3D;org.hibernate.dialect.MySQL5InnoDBDialect</span><br><span class="line">spring.jpa.show-sql&#x3D;true</span><br></pre></td></tr></table></figure><p>其实这个hibernate.hbm2ddl.auto参数的作用主要用于：自动创建|更新|验证数据库表结构，有四个值：</p><ol><li>create：每次加载hibernate时都会删除上一次生成的表，然后根据model类再重新生成新表，哪怕两次没有任何改变也会这样执行，这就是导致数据库数据丢失的一个重要原因。</li><li>create-drop：每次加载hibernate时根据model类生成表，但是sessionFactory一关闭，表就自动删除。</li><li>update：最常用的属性，第一次加载hibernate时根据model类会自动建立表的结构（数据库先建好），以后加载hibernate时根据model类自动更新表结构，即使表结构改变了但表中的行仍然存在不会被删除。要留意的是当部署到服务器后，表结构是不会被马上建立ilai的，是要等应用第一次运行起来后才会。</li><li>validate：每次加载hibernate时，验证创建数据库表结构，只会和数据库中的表进行比较，不会创建新表，但是会插入新值。</li></ol><p><code>dialect</code> 主要是指定生成表名的存储引擎为InneoDB<br><code>show-sql</code> 是否打印出自动生成的SQL,方便调试的时候查看</p><h3 id="3-添加实体类和Dao"><a href="#3-添加实体类和Dao" class="headerlink" title="3. 添加实体类和Dao"></a>3. 添加实体类和Dao</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Entity</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">User</span> <span class="keyword">implements</span> <span class="title">Serializable</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> serialVersionUID = <span class="number">1L</span>;</span><br><span class="line">    <span class="meta">@Id</span></span><br><span class="line">    <span class="meta">@GeneratedValue</span></span><br><span class="line">    <span class="keyword">private</span> Long id;</span><br><span class="line">    <span class="meta">@Column</span>(nullable = <span class="keyword">false</span>, unique = <span class="keyword">true</span>)</span><br><span class="line">    <span class="keyword">private</span> String userName;</span><br><span class="line">    <span class="meta">@Column</span>(nullable = <span class="keyword">false</span>)</span><br><span class="line">    <span class="keyword">private</span> String passWord;</span><br><span class="line">    <span class="meta">@Column</span>(nullable = <span class="keyword">false</span>, unique = <span class="keyword">true</span>)</span><br><span class="line">    <span class="keyword">private</span> String email;</span><br><span class="line">    <span class="meta">@Column</span>(nullable = <span class="keyword">true</span>, unique = <span class="keyword">true</span>)</span><br><span class="line">    <span class="keyword">private</span> String nickName;</span><br><span class="line">    <span class="meta">@Column</span>(nullable = <span class="keyword">false</span>)</span><br><span class="line">    <span class="keyword">private</span> String regTime;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//省略getter settet方法、构造方法</span></span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>dao只要继承JpaRepository类就可以，几乎可以不用写方法，还有一个特别的功能非常赞，就是根据方法名来自动生成SQL,比如<code>findByUserName</code>，会自动产生一个以<code>userName</code>为参数的查询方法，再比如<code>findAll</code>就会查询表里面的所有数据，再比如自动分页等等。</p><p><strong>Entity中不映射成列的字段得加@Transient注解，不加注解就会映射成列</strong><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></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">UserRepository</span> <span class="keyword">extends</span> <span class="title">JpaRepository</span>&lt;<span class="title">User</span>,<span class="title">Long</span>&gt; </span>&#123;</span><br><span class="line">    <span class="function">User <span class="title">findByUserName</span><span class="params">(String userName)</span></span>;</span><br><span class="line">    <span class="function">User <span class="title">findByUserNameOrEmail</span><span class="params">(String userName,String email)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><h3 id="4-测试"><a href="#4-测试" class="headerlink" title="4. 测试"></a>4. 测试</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@RunWith</span>(SpringJUnit4ClassRunner<span class="class">.<span class="keyword">class</span>)</span></span><br><span class="line"><span class="class">@<span class="title">SpringBootTest</span></span></span><br><span class="line"><span class="class"><span class="title">public</span> <span class="title">class</span> <span class="title">UserRepositoryTests</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> UserRepository userRepository;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">test</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        Date date = <span class="keyword">new</span> Date();</span><br><span class="line">        DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG);</span><br><span class="line">        String formattedDate = dateFormat.format(date);</span><br><span class="line"></span><br><span class="line">        userRepository.save(<span class="keyword">new</span> User(<span class="string">"aa1"</span>, <span class="string">"aa@gmail.com"</span>, <span class="string">"aa"</span>, <span class="string">"aa123456"</span>, formattedDate));</span><br><span class="line">        userRepository.save(<span class="keyword">new</span> User(<span class="string">"bb1"</span>, <span class="string">"bb@gmail.com"</span>, <span class="string">"bb"</span>, <span class="string">"bb123456"</span>, formattedDate));</span><br><span class="line">        userRepository.save(<span class="keyword">new</span> User(<span class="string">"cc1"</span>, <span class="string">"cc@gmail.com"</span>, <span class="string">"cc"</span>, <span class="string">"cc123456"</span>, formattedDate));</span><br><span class="line"></span><br><span class="line">        Assert.assertEquals(<span class="number">3</span>,userRepository.findAll().size());</span><br><span class="line">        Assert.assertEquals(<span class="string">"bb"</span>,userRepository.findByUserNameOrEmail(<span class="string">"bb"</span>,<span class="string">"bb@gmail.com"</span>).getNickName());</span><br><span class="line">        userRepository.delete(userRepository.findByUserName(<span class="string">"aa1"</span>));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>当然spring data jpa还有很多功能，比如封装好的分页，可以自定义SQL，主从分离等等，等到以后再做细讲。</p><h2 id="Thymeleaf模板"><a href="#Thymeleaf模板" class="headerlink" title="Thymeleaf模板"></a>Thymeleaf模板</h2><p>Spring Boot推荐使用thymeleaf模板来代替jpa,那么thymeleaf模板到底好在哪里呢，让Spring官方来推荐，下面我们来看看</p><h3 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h3><p>Thymeleaf是一款用于渲染XML/XHTML/HTML5内容的模板引擎。类似JSP，Velocity，FreeMarker等，它也可以轻易的与Spring MVC等Web框架进行集成作为Web应用的模板引擎。与其它模板引擎相比，<strong>Thymeleaf最大的特点是能够直接在浏览器中打开并正确显示模板页面，而不需要启动整个Web应用</strong>。</p><p>好了，你们可能会觉得已经习惯使用了velocity,freemarker,beetle之类的模板，那么thymeleaf到底好在哪呢？让我们来做个对比：<br>Thymeleaf是与众不同的，因为它使用了自然的模板技术。这意味着Thymeleaf的模板语法并不会破坏文档的结构，模板依旧是有效的XML文档。模板还可以用作工作原型，Thymeleaf会在运行期替换掉静态值。Velocity与FreeMarker则是连续的文本处理器。</p><p>下面的代码示例分别使用Velocity、FreeMarker与Thymeleaf打印出一条消息：<br></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></pre></td><td class="code"><pre><span class="line">Velocity: <span class="tag">&lt;<span class="name">p</span>&gt;</span>$message<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line">FreeMarker: <span class="tag">&lt;<span class="name">p</span>&gt;</span>$&#123;message&#125;<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line">Thymeleaf: <span class="tag">&lt;<span class="name">p</span> <span class="attr">th:text</span>=<span class="string">"$&#123;message&#125;"</span>&gt;</span>Hello World!<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br></pre></td></tr></table></figure><br><strong>注意，由于Thymeleaf使用了XML DOM解析器，因此它并不适合处理大规模的XML文件。</strong><p></p><h3 id="简单用法"><a href="#简单用法" class="headerlink" title="简单用法"></a>简单用法</h3><p><strong>URL</strong></p><p>URL在Web应用模板中占据着十分重要的地位，需要特别注意 的是Thymeleaf对于URL的处理是通过语法@{…}来处理的。Thymeleaf支持绝对路径URL:<br></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">a</span> <span class="attr">th:href</span>=<span class="string">"@&#123;https://www.baidu.com&#125;"</span>&gt;</span>百度<span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br></pre></td></tr></table></figure><p></p><p><strong>条件求值</strong><br></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">a</span> <span class="attr">th:href</span>=<span class="string">"@&#123;/login&#125;"</span> <span class="attr">th:unless</span>=<span class="string">"$&#123;session.user != null&#125;"</span>&gt;</span>登录<span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br></pre></td></tr></table></figure><p></p><p><strong>for循环</strong><br></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">tr</span> <span class="attr">th:each</span>=<span class="string">"prod : $&#123;prods&#125;"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">td</span> <span class="attr">th:text</span>=<span class="string">"$&#123;prod.getName()&#125;"</span>&gt;</span>Onions<span class="tag">&lt;/<span class="name">td</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">td</span> <span class="attr">th:text</span>=<span class="string">"$&#123;prod.getPrice()&#125;"</span>&gt;</span>2.41<span class="tag">&lt;/<span class="name">td</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">td</span> <span class="attr">th:text</span>=<span class="string">"$&#123;prod.isInStock&#125; ? #&#123;true&#125; : #&#123;false&#125;"</span>&gt;</span>yes<span class="tag">&lt;/<span class="name">td</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">tr</span>&gt;</span></span><br></pre></td></tr></table></figure><br>暂时就列这几个<p></p><h3 id="页面即原型"><a href="#页面即原型" class="headerlink" title="页面即原型"></a>页面即原型</h3><p>在Web开发过程中一个绕不开的话题就是前端工程师与后端工程师的协作，在传统Java Web开发过程中，前端工程师和后端工程师一样，也需要安装一套完整的开发环境，然后各类Java IDE中修改模板、静态资源文件，启动/重启/重新加载应用服务器，刷新页面查看最终效果。</p><p>但实际上前端工程师的职责更多应该关注于页面本身而非后端，使用JSP，Velocity等传统的Java模板引擎很难做到这一点，因为它们必须在应用服务器中渲染完成后才能在浏览器中看到结果，而Thymeleaf从根本上颠覆了这一过程，通过属性进行模板渲染不会引入任何新的浏览器不能识别的标签，例如JSP中的，不会在Tag内部写表达式。整个页面直接作为HTML文件用浏览器打开，几乎就可以看到最终的效果，这大大解放了前端工程师的生产力，它们的最终交付物就是纯的HTML/CSS/JavaScript文件。</p><h2 id="WebJars"><a href="#WebJars" class="headerlink" title="WebJars"></a>WebJars</h2><p>WebJars是一个很神奇的东西，可以让我们以jar包的形式来使用前端的各种框架和组件。</p><h3 id="什么是Webjars"><a href="#什么是Webjars" class="headerlink" title="什么是Webjars"></a>什么是Webjars</h3><p>什么是WebJars？WebJars是将客户端（浏览器）资源（JavaScript，Css等）打成jar包文件，以对资源进行统一依赖管理。WebJars的jar包部署在Maven中央仓库上。</p><h3 id="为什么使用"><a href="#为什么使用" class="headerlink" title="为什么使用"></a>为什么使用</h3><p>我们在开发Java web项目的时候会使用像Maven，Gradle等构建工具以实现对jar包版本依赖管理，以及项目的自动化管理，但是对于JavaScript，Css等前端资源包，我们只能采用拷贝到webapp下的方式，这样做就无法对这些资源进行依赖管理。那么WebJars就提供给我们这些前端资源的jar包形势，我们就可以进行依赖管理。</p><h3 id="怎样使用"><a href="#怎样使用" class="headerlink" title="怎样使用"></a>怎样使用</h3><ol><li><a href="https://www.webjars.org/" target="_blank" rel="external nofollow noopener noreferrer">WebJars官网</a>查找对应的组件，比如Bootstrap.js<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.webjars.bower<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>bootstrap<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>3.3.6<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></li><li>在页面引入<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">link</span> <span class="attr">th:href</span>=<span class="string">"@&#123;/webjars/bootstrap/3.3.6/dist/css/bootstrap.css&#125;"</span> <span class="attr">rel</span>=<span class="string">"stylesheet"</span>&gt;</span><span class="tag">&lt;/<span class="name">link</span>&gt;</span></span><br></pre></td></tr></table></figure></li></ol><p>就可以正常使用了。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;/images/spring-springboot-1.jpg&quot; alt=&quot;spring-springboot-web&quot;&gt;&lt;/p&gt;&lt;p&gt;Spring Boot Web开发非常的简单，其中包括常用的json输出、filters、property、log等。&lt;br&gt;
    
    </summary>
    
    
      <category term="SpringBoot" scheme="http://blog.develophelper.com/categories/springboot/"/>
    
    
      <category term="SpringBoot" scheme="http://blog.develophelper.com/tags/springboot/"/>
    
      <category term="Web综合开发" scheme="http://blog.develophelper.com/tags/web%E7%BB%BC%E5%90%88%E5%BC%80%E5%8F%91/"/>
    
  </entry>
  
  <entry>
    <title>SpringBoot实战（一）入门</title>
    <link href="http://blog.develophelper.com/article/spring-springboot-helloword.html"/>
    <id>http://blog.develophelper.com/article/spring-springboot-helloword.html</id>
    <published>2018-09-01T13:42:56.000Z</published>
    <updated>2020-02-15T15:23:10.033Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/images/spring-springboot-1.jpg" alt="spring-springboot-helloworld"></p><p>Spring Boot 是由 Pivotal 团队提供的全新框架，其设计目的是用来简化新 Spring 应用的初始搭建以及开发过程，该框架使用了特定的方式来进行配置，从而使开发人员不再需要定义样板化的配置。Spring Boot 默认配置了很多框架的使用方式，就像 Maven 整合了所有的 Jar 包，Spring Boot 整合了所有的框架。它的核心设计思想是：<strong>约定优于配置</strong>，Spring Boot 所有开发细节都是依据此思想进行实现的。<br><a id="more"></a></p><p>Spring Boot 是一套全新的框架，它来自于 Spring 大家族，因此 Spring 所有具备的功能它都有并且更容易使用；同时还简化了基于 Spring 的应用开发，通过少量的代码就能创建一个独立的、产品级别的 Spring 应用。</p><p>下图展示出了 Spring Boot 在 Spring 生态中的位置：<br><img src="/images/spring-springboot-helloworld-2.png" alt="spring-springboot-helloworld"></p><p>该项目主要的目的是：</p><ul><li>上手 Spring 开发更快、更广泛；</li><li>使用默认方式实现快速开发；</li><li>提供大多数项目所需的非功能特性，诸如：嵌入式服务器、安全、心跳检查、外部配置等。</li></ul><p><strong>Spring Boot 特性</strong></p><ul><li>使用 Spring 项目引导页面可以在几秒构建一个项目；</li><li>方便对外输出各种形式的服务，如 REST API、WebSocket、Web、Streaming、Tasks；</li><li>非常简洁的安全策略集成；</li><li>支持关系数据库和非关系数据库；</li><li>支持运行期内嵌容器，如 Tomcat、Jetty；</li><li>强大的开发包，支持热启动；</li><li>自动管理依赖；</li><li>自带应用监控；</li><li>支持各种 IDE，如 IntelliJ IDEA、NetBeans。</li></ul><h2 id="为什么学习-Spring-Boot-？"><a href="#为什么学习-Spring-Boot-？" class="headerlink" title="为什么学习 Spring Boot ？"></a>为什么学习 Spring Boot ？</h2><p>Spring Boot 本身并不提供 Spring 框架的核心特性以及扩展功能，只是用于快速、敏捷地开发新一代基于 Spring 框架的应用程序。同时它集成了大量常用的第三方库配置（如 Redis、MongoDB、JPA、RabbitMQ、Quartz 等），Spring Boot 应用中这些第三方库几乎可以零配置进行开箱即用，大部分的 Spring Boot 应用都只需要非常少量的配置代码，开发者能够更加专注于业务逻辑。</p><p>使用 Spring Boot 开发项目，有以下几方面优势：</p><ol><li>Spring Boot 使开发变得简单，提供了丰富的解决方案，快速集成各种解决方案提升开发效率。</li><li>Spring Boot 使配置变得简单，提供了丰富的 Starters，集成主流开源产品往往只需要简单的配置即可。</li><li>Spring Boot 使部署变得简单，其本身内嵌启动容器，仅仅需要一个命令即可启动项目，结合 Jenkins、Docker 自动化运维非常容易实现。</li><li>Spring Boot 使监控变得简单，自带监控组件，使用 Actuator 轻松监控服务各项状态。</li></ol><p>从软件发展的角度来讲，越简单的开发模式越流行，简单的开发模式解放出更多生产力，让开发人员可以避免将精力耗费在各种配置、语法所设置的门槛上，从而更专注于业务。这点上，Spring Boot 已尽可能地简化了应用开发的门槛。</p><p>Spring Boot 所集成的技术栈，涵盖了各大互联网公司的主流技术，跟着 Spring Boot 的路线去学习，基本可以了解国内外互联网公司的技术特点。</p><h2 id="快速入门"><a href="#快速入门" class="headerlink" title="快速入门"></a>快速入门</h2><p>说了那么多，手痒痒的很，马上来一发试试。</p><h3 id="maven-构建项目"><a href="#maven-构建项目" class="headerlink" title="maven 构建项目"></a>maven 构建项目</h3><ol><li>访问<a href="http://start.spring.io/" target="_blank" rel="external nofollow noopener noreferrer">http://start.spring.io/</a></li><li>选择构建工具Maven Project、Spring Boot版本1.5.16以及一些工程基本信息，点击“Switch to the full version.”java版本选择1.8，可参考下图所示：<br><img src="/images/spring-springboot-helloworld-3.png" alt="spring-springboot-helloworld"></li><li>点击Generate Project下载项目压缩包</li><li>解压后，使用Idea，Open -&gt; 选择解压后的文件夹中的pom.xml文件 -&gt; Open as Project</li></ol><h3 id="项目结构介绍"><a href="#项目结构介绍" class="headerlink" title="项目结构介绍"></a>项目结构介绍</h3><p><img src="/images/spring-springboot-helloworld-4.png" alt="spring-springboot-helloworld"><br>如上图所示，Spring Boot的基础结构共三个文件：</p><ul><li>src/main/java 程序开发以及主程序入口</li><li>src/main/resources 配置文件</li><li>src/test/java 测试程序</li></ul><p>另外，Spring Boot建议的目录结构如下：</p><p>root package结构：com.example.myproject</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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">com</span><br><span class="line">  +- example</span><br><span class="line">    +- myproject</span><br><span class="line">      +- Application.java</span><br><span class="line">      |</span><br><span class="line">      +- domain</span><br><span class="line">      |  +- Customer.java</span><br><span class="line">      |  +- CustomerRepository.java</span><br><span class="line">      |</span><br><span class="line">      +- service</span><br><span class="line">      |  +- CustomerService.java</span><br><span class="line">      |</span><br><span class="line">      +- controller</span><br><span class="line">      |  +- CustomerController.java</span><br><span class="line">      |</span><br></pre></td></tr></table></figure><ol><li>Application.java 建议放到根目录瞎买，主要用于做一些框架配置</li><li>domain 目录主要用于实体（Entity）和数据访问层（Repository）</li><li>service 层主要是业务逻辑代码</li><li>controller 负责页面访问控制</li></ol><p>采用默认配置可以省去很多配置，当然也可以根据自己的喜欢来进行更改</p><p>最后，启动Application main方法，至此一个java项目搭建好了。</p><h3 id="引入web模块"><a href="#引入web模块" class="headerlink" title="引入web模块"></a>引入web模块</h3><ol><li>在pom.xml文件中添加支持web的模块：<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-web<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure></li></ol><p>pom.xml 文件中默认有两个模块：</p><ul><li>spring-boot-starter: 核心模块，包括自动配置支持、日志和YAML;</li><li>spring-boot-starter-test: 测试模块，包括JUnit、Hamcrest、Mockito;</li></ul><ol><li><p>编写controller内容</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">HelloWorldController</span> </span>&#123;</span><br><span class="line">    <span class="meta">@GetMapping</span>(<span class="string">"/hello"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">index</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">"Hello World."</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>@RestController的作用就是controller里面的方法都以json格式输出，不用再写jackson配置，其本质实际上就是@Controller + @ResponseBody。</p></li><li><p>启动主程序，打开浏览器访问<a href="http://localhost:8080/hello，就可以看到效果了，有木有很简单！" target="_blank" rel="external nofollow noopener noreferrer">http://localhost:8080/hello，就可以看到效果了，有木有很简单！</a></p></li></ol><h3 id="单元测试"><a href="#单元测试" class="headerlink" title="单元测试"></a>单元测试</h3><p>打开scr/test/java 下的测试入口，编写简单的http请求来测试：使用mockmvc实现，并利用MockMcvResultHandlers.print()打印出执行结果。<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><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@RunWith</span>(SpringRunner<span class="class">.<span class="keyword">class</span>)</span></span><br><span class="line"><span class="class">@<span class="title">SpringBootTest</span></span></span><br><span class="line"><span class="class"><span class="title">public</span> <span class="title">class</span> <span class="title">HelloWorldControllerTests</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> MockMvc mvc;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Before</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setUp</span><span class="params">()</span> <span class="keyword">throws</span> Exception</span>&#123;</span><br><span class="line">        mvc = MockMvcBuilders.standaloneSetup(<span class="keyword">new</span> HelloWorldController()).build();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">getHello</span><span class="params">()</span> <span class="keyword">throws</span> Exception</span>&#123;</span><br><span class="line">        mvc.perform(MockMvcRequestBuilders.get(<span class="string">"/hello"</span>).accept(MediaType.APPLICATION_JSON))</span><br><span class="line">                .andExpect(MockMvcResultMatchers.status().isOk())</span><br><span class="line">                .andDo(MockMvcResultHandlers.print())</span><br><span class="line">                .andReturn();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><h3 id="热启动"><a href="#热启动" class="headerlink" title="热启动"></a>热启动</h3><p>热启动在正常开发项目中已经很常见了，虽然平时开发web项目过程中，改动项目后重启总是报错，但是Spring Boot对调试支持很好，修改之后可以实时生效，需要添加以下配置：<br></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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-devtools<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">optional</span>&gt;</span>true<span class="tag">&lt;/<span class="name">optional</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">build</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">plugins</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">plugin</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-maven-plugin<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">configuration</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">fork</span>&gt;</span>true<span class="tag">&lt;/<span class="name">fork</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">configuration</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">plugin</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;/<span class="name">plugins</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">build</span>&gt;</span></span><br></pre></td></tr></table></figure><br>使用idea还需要更改一些配置，setting修改，如下图：<br><img src="/images/spring-springboot-helloworld-5.png" alt="spring-springboot-helloworld"><br>然后按组合键 Shift+Ctrl+Alt+/ 选择Registry… 找到 compiler.automake.allow.when.app.running 勾选即可。<p></p><p>热启动模块在完整的打包环境下运行的时候会被禁用。如果你使用java -jar启动应用或者用一个特定的classloader启动，它会认为这是一个“生产环境”。</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>使用spring boot可以非常方便、快速搭建项目，使我们不用关心框架之间的兼容性，适用版本等各种问题，我们想使用任何东西，仅仅添加一个配置就可以，所以使用sping boot非常适合构建微服务。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;/images/spring-springboot-1.jpg&quot; alt=&quot;spring-springboot-helloworld&quot;&gt;&lt;/p&gt;&lt;p&gt;Spring Boot 是由 Pivotal 团队提供的全新框架，其设计目的是用来简化新 Spring 应用的初始搭建以及开发过程，该框架使用了特定的方式来进行配置，从而使开发人员不再需要定义样板化的配置。Spring Boot 默认配置了很多框架的使用方式，就像 Maven 整合了所有的 Jar 包，Spring Boot 整合了所有的框架。它的核心设计思想是：&lt;strong&gt;约定优于配置&lt;/strong&gt;，Spring Boot 所有开发细节都是依据此思想进行实现的。&lt;br&gt;
    
    </summary>
    
    
      <category term="SpringBoot" scheme="http://blog.develophelper.com/categories/springboot/"/>
    
    
      <category term="SpringBoot" scheme="http://blog.develophelper.com/tags/springboot/"/>
    
      <category term="HelloWorld" scheme="http://blog.develophelper.com/tags/helloworld/"/>
    
  </entry>
  
  <entry>
    <title>什么是H.265</title>
    <link href="http://blog.develophelper.com/article/image-audio-what-is-H.265.html"/>
    <id>http://blog.develophelper.com/article/image-audio-what-is-H.265.html</id>
    <published>2018-07-16T02:26:00.000Z</published>
    <updated>2020-02-15T15:23:10.032Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/images/h.265_1.jpg" alt="xh265-vid-top jpg pagespeed gp jp jw pj ws js rj rp rw ri cp md ic saoisp96-x"></p><p>最近偶然读了一篇文章<a href="https://mp.weixin.qq.com/s/z5y4NUr_lE_cY6FMSaIRUw" target="_blank" rel="external nofollow noopener noreferrer">一位女运维的自述：3年为公司节省10亿元！</a>，大意是腾讯运维如何运用图像影音压缩技术为公司和用户节省了大量流量，节约了成本并提高了用户体验。其中关键技术 <strong><em>H.265</em></strong> 吸引了我的注意，于是百谷歌度了一番，做了一个大致的了解。</p><a id="more"></a><p><a href="https://www.howtogeek.com/206662/should-you-get-an-ultra-hd-4k-tv/" target="_blank" rel="external nofollow noopener noreferrer">4K is the next big thing in TVs</a>, 和 <a href="https://www.howtogeek.com/300847/where-can-you-find-4k-video-for-your-4k-tv/" target="_blank" rel="external nofollow noopener noreferrer">4K videos are starting to pop up everywhere</a>. 但由于4K视频占用了大量的空间，很难以最佳质量进行下载和流式传输，而值得庆幸的是，一种被称为高效视频编码技术（High Efficiency Video Coding (HEVC), or H.265）正在改变这种情况。</p><p>这项新技术还需要很长一段时间才能得到大范围的应用，目前它正出现在—4K UHD Blu-rays 使用 HEVC, <a href="https://www.videolan.org/vlc/releases/3.0.0.html" target="_blank" rel="external nofollow noopener noreferrer">VLC 3.0</a>使得你PC机上的HEVC 和 4K 视频更加清晰，iPhone 设备甚至可以用HEVC技术记录视频以节约存储空间。但它是如何工作的，为什么对4K视频如此重要？</p><h2 id="1-当前标准：AVC-H-264"><a href="#1-当前标准：AVC-H-264" class="headerlink" title="1. 当前标准：AVC / H.264"></a>1. 当前标准：AVC / H.264</h2><p>当你观看蓝光光盘，YouTube视频或者来自ITunes的电影时，它与来自编辑室的原始视频不是完全相同的。为了是该电影或者视频更适合蓝光光盘——或者使其足够小以便在网上轻松下载——必须对电影压缩。<br>高级视频编码也被称为AVC或H.264,是广泛使用的视频压缩的最佳标准，并且有几种不同的方法可用于减小视频文件的大小。<br>例如，在任何给定的帧中，它可以查找大多数颜色相同的区域。在我和我儿子的这幅禁止帧中——大部分的天空都是相同的蓝色，因此压缩算法可以将图像分割成块——称之为“macroblocks”——并表明“我们仅仅假设沿着顶部的所有这些块都是相同的蓝色以替代记录每个像素的颜色”。这比存储每个单独像素的颜色效率更高，降低最终图像的文件大小。在视频中， 该技术被称之为帧内压缩——压缩单个帧的数据。</p><p><img src="/images/h.265_2.jpg" alt="img_1617-1"></p><p>AVC还使用帧间压缩，它可以查看多个帧并记录帧的哪些部分正在改变 - 哪些不是。从“美国队长：内战”中获得这一镜头。 背景并没有太大的变化 - 帧之间的差异大部分来自钢铁侠的脸部和身体。 所以，压缩算法可以将帧分成相同的宏块，并表明“你知道什么？ 这些块不会改变100帧，所以让我们再次显示它们，而不是将整个图像存储100次。“这可以显著减小文件大小。</p><p><img src="/images/h.265_3.gif" alt="5af0120085a6c_5af012009aeab"></p><p>这些只是AVC / H.264使用方法的两个过度简化的例子，但您清楚了其中的原理。 这完全是为了在不影响质量的情况下提高视频文件的效率。 （当然，如果压缩太多，任何视频都会失去质量，但这些技术越聪明，在您进入该点之前就可以压缩越多。）</p><h2 id="2-HEVC-H-265更高效地压缩视频，非常适合4K视频"><a href="#2-HEVC-H-265更高效地压缩视频，非常适合4K视频" class="headerlink" title="2. HEVC / H.265更高效地压缩视频，非常适合4K视频"></a>2. HEVC / H.265更高效地压缩视频，非常适合4K视频</h2><p>高效视频编码技术也称为HEVC或H.265，是视频编码技术演进的下一步。 它的构建使用了AVC / H.264中的许多技术，使视频压缩效率更高。</p><p>举个例子，当AVC查看多个帧变更时（例如上面的美国队长例子），这些宏块“块”可以是几个不同的形状和大小，最多可达16个像素乘16个像素。 而使用HEVC，这些块可以达到64×64的尺寸 - 远远大于16×16，这意味着该算法可以记忆更少的块，从而减小整体视频的尺寸。</p><p>您可以在<a href="https://www.youtube.com/channel/UCD80RKxQODrPv-PdM3Js8IQ" target="_blank" rel="external nofollow noopener noreferrer">HandyAndy Tech Tips</a>的这个精彩视频中看到对这项技术的更多技术性解释.</p><p>当然，HEVC还有其他一些技术正在运用，但这是最大的改进之一 - 当所有事情说到做到之后，HEVC可以在相同的质量水平下将视频压缩两倍于AVC。 对于用AVC技术编码而占用大量空间的4K视频而尤其重要。 HEVC使4K视频更容易流式传输，下载或翻录到硬盘。</p><h2 id="3-缺陷：HEVC在没有硬件加速解码的情况下很慢"><a href="#3-缺陷：HEVC在没有硬件加速解码的情况下很慢" class="headerlink" title="3. 缺陷：HEVC在没有硬件加速解码的情况下很慢"></a>3. 缺陷：HEVC在没有硬件加速解码的情况下很慢</h2><p>自2013年以来，HEVC一直是获批准的标准，那么为什么我们不能将它用于所有视频？<br>相关： <a href="https://www.howtogeek.com/260784/how-to-make-vlc-use-less-battery-life-by-enabling-hardware-acceleration/" target="_blank" rel="external nofollow noopener noreferrer">如何通过启用硬件加速使VLC使用更少的电量</a><br>这些压缩算法非常复杂 - 在视频播放之前，需要花费大量的数学计算才能实现解码。 计算机有两种主要的方式可以解码在这种视频：一软件解码，它会使用你的计算机的CPU来完成这个数学运算，或者二<a href="https://www.howtogeek.com/260784/how-to-make-vlc-use-less-battery-life-by-enabling-hardware-acceleration/" target="_blank" rel="external nofollow noopener noreferrer">硬件解码</a> ，在这个解码过程中，它将负载交给你的图形卡（或者你的集成图形芯片中央处理器）。 只要显卡支持所尝试播放的视频的编解码器，则效率更高。</p><p>因此，尽管许多PC和程序都可以尝试播放HEVC视频，但是如果没有硬件解码，它可能会卡顿或者非常慢。 所以，除非你有一个支持HEVC硬件解码的图形卡和视频播放器，否则HEVC对你来说并不是很好。</p><p>这对于独立播放设备来说不是问题 - 包括Xbox One在内的4K蓝光播放机都是以HEVC为基础构建的。 但是当谈到在PC上播放HEVC视频时，事情变得更加困难。 您的计算机将需要以下硬件之一才能硬解码HEVC视频：</p><ul><li>英特尔第六代“Skylake”或更新的CPU</li><li>AMD第六代“Carizzo”或更新的APU</li><li>NVIDIA GeForce GTX 950,960或更新的显卡</li><li>AMD Radeon R9 Fury，R9 Fury X，R9 Nano或更新的显卡</li></ul><p>您可能还需要使用不仅能支持HEVC视频，而且还支持HEVC硬件解码的操作系统和视频播放器 - 目前这有点多余。 许多玩家仍在增加对HEVC硬件解码的支持，并且在某些情况下，它可能仅适用于上面列表中的某些芯片。 在撰写本文时， <a href="https://www.videolan.org/" target="_blank" rel="external nofollow noopener noreferrer">VLC</a> 3.0， <a href="https://kodi.tv/" target="_blank" rel="external nofollow noopener noreferrer">Kodi</a> 17和<a href="https://www.plex.tv/" target="_blank" rel="external nofollow noopener noreferrer">Plex Media Server</a> 1.10都支持某种形式的HEVC硬件解码，至少对于某些卡而言。 不过，您可能必须在选择的播放器中<a href="https://www.howtogeek.com/260784/how-to-make-vlc-use-less-battery-life-by-enabling-hardware-acceleration/" target="_blank" rel="external nofollow noopener noreferrer">启用硬件加速</a>才能正常工作。</p><p>随着时间的推移，越来越多的计算机将能够处理这种视频，而更多的播放器将会更广泛地支持它 - 就像现在使用AVC / H.264一样。 它可能需要一段时间才会变得无处不在，在此之前，您必须以巨大的文件大小（或压缩它并丢失图像质量）将您的4K视频存储在AVC / H.264中。 但是，HEVC / H.265得到广泛支持越多，视频就越好。</p><h2 id="4-参考"><a href="#4-参考" class="headerlink" title="4.参考"></a>4.参考</h2><p>图片来源： <a href="https://www.shutterstock.com/image-photo/realism-sporting-images-broadcast-on-tv-725299213" target="_blank" rel="external nofollow noopener noreferrer">alphaspirit</a> /Shutterstock.com</p><p>本文翻译自：<a href="https://www.howtogeek.com/342416/what-is-hevc-h.265-video-and-why-is-it-so-important-for-4k-movies/" target="_blank" rel="external nofollow noopener noreferrer">What Is HEVC H.265 Video, and Why Is It So Important for 4K Movies?</a></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;/images/h.265_1.jpg&quot; alt=&quot;xh265-vid-top jpg pagespeed gp jp jw pj ws js rj rp rw ri cp md ic saoisp96-x&quot;&gt;&lt;/p&gt;&lt;p&gt;最近偶然读了一篇文章&lt;a href=&quot;https://mp.weixin.qq.com/s/z5y4NUr_lE_cY6FMSaIRUw&quot; target=&quot;_blank&quot; rel=&quot;external nofollow noopener noreferrer&quot;&gt;一位女运维的自述：3年为公司节省10亿元！&lt;/a&gt;，大意是腾讯运维如何运用图像影音压缩技术为公司和用户节省了大量流量，节约了成本并提高了用户体验。其中关键技术 &lt;strong&gt;&lt;em&gt;H.265&lt;/em&gt;&lt;/strong&gt; 吸引了我的注意，于是百谷歌度了一番，做了一个大致的了解。&lt;/p&gt;
    
    </summary>
    
    
      <category term="图像影音" scheme="http://blog.develophelper.com/categories/%E5%9B%BE%E5%83%8F%E5%BD%B1%E9%9F%B3/"/>
    
    
      <category term="随手写写" scheme="http://blog.develophelper.com/tags/%E9%9A%8F%E6%89%8B%E5%86%99%E5%86%99/"/>
    
  </entry>
  
  <entry>
    <title>java基础（二）封装</title>
    <link href="http://blog.develophelper.com/article/java-base-object-oriented-encapsulation.html"/>
    <id>http://blog.develophelper.com/article/java-base-object-oriented-encapsulation.html</id>
    <published>2018-06-02T13:42:56.000Z</published>
    <updated>2020-02-15T15:23:10.032Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/images/encapsulation_2.png" alt="encapsulation"></p><p>对于面向对象的特点，想必大家应该都可以倒背如流：封装，继承，多态。但很多人对这些特点的理解仅仅停留在表面，认为封装就是变量的私有化，然后对外开放接口，获取和设置值，而不知道为什么要这样做。<br><a id="more"></a><br>封装，简单来说就是将变量私有化，在java里的用的就是private修饰符修饰，这样在外部产生的对象就不能直接访问这个变量。想要外部对象对变量进行访问或操作，就需要在类里面提供外部访问的接口，也就是我们熟知的get和set方法。</p><p>以上就是大部分人对于封装的理解。知道有封装这回事，知道怎么用，却不知道为什么要用，甚至觉得多此一举。因为明明person.name就是访问到变量，为什么非要person.getName()呢？</p><h1 id="1-任性的使用public"><a href="#1-任性的使用public" class="headerlink" title="1. 任性的使用public"></a>1. 任性的使用public</h1><p>让我们先来看一下不使用封装的情况：</p><p>设计了3个类，人、男人、女人</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></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">Person</span></span>&#123;</span><br><span class="line">  <span class="keyword">public</span> String name;</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">int</span> age;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 省略get和set方法</span></span><br><span class="line">&#125;</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">Man</span> <span class="keyword">extends</span> <span class="title">Person</span></span>&#123;</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">double</span> money;</span><br><span class="line">  <span class="keyword">public</span> Woman wife;</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// 省略get和set方法</span></span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">marry</span><span class="params">(Woman woman)</span></span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.wife = woman;</span><br><span class="line">    woman.marry(<span class="keyword">this</span>);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Woman</span> <span class="keyword">extends</span> <span class="title">Person</span></span>&#123;</span><br><span class="line">  <span class="keyword">public</span> Man husband;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 省略get和set方法</span></span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">marry</span><span class="params">(Man man)</span></span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.husband = man;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Test</span></span>&#123;</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">    Man man = <span class="keyword">new</span> Man();</span><br><span class="line">    man.name = <span class="string">"snail"</span>;</span><br><span class="line">    man.age = <span class="number">30</span>;</span><br><span class="line">    man.money = <span class="number">10000</span>;</span><br><span class="line"></span><br><span class="line">    Woman woman = <span class="keyword">new</span> Woman();</span><br><span class="line">    woman.name = <span class="string">"lucy"</span>;</span><br><span class="line">    </span><br><span class="line">    man.marry(woman);</span><br><span class="line"></span><br><span class="line">    System.out.println(man.name + <span class="string">"的妻子"</span> + man.wife.name); <span class="comment">//snail的妻子：lucy</span></span><br><span class="line">    System.out.println(man.name + <span class="string">"的钱："</span> + man.money); <span class="comment">//snail的钱：10000.0</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>到这里一切正常，看起来也还不错。</p><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><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></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">Thief</span> <span class="keyword">extends</span> <span class="title">Man</span></span>&#123;</span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">double</span> stealMoney = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">private</span> List&lt;Woman&gt; women = <span class="keyword">new</span> ArrayList&lt;Woman&gt;();</span><br><span class="line"></span><br><span class="line">  <span class="comment">//偷钱</span></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">stealMoney</span><span class="params">(Man man)</span></span>&#123;</span><br><span class="line">    stealMoney += man.money;</span><br><span class="line">    man.money = <span class="number">0</span>;</span><br><span class="line">    </span><br><span class="line">    System.out.println(<span class="string">"哈哈，偷到钱了..."</span>);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">//偷老婆</span></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">stealWife</span><span class="params">(Man man)</span></span>&#123;</span><br><span class="line">    man.wife.husband = <span class="keyword">this</span>;</span><br><span class="line">    women.add(man.wife);</span><br><span class="line">    </span><br><span class="line">    Woman woman = <span class="keyword">new</span> Woman();</span><br><span class="line">    woman.name = <span class="string">"凤姐"</span>;</span><br><span class="line">    man.wife = woman;</span><br><span class="line"></span><br><span class="line">    System.out.println(<span class="string">"哈哈哈，又偷了一个妹子做老婆..."</span>);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Test</span></span>&#123;</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">    Man man = <span class="keyword">new</span> Man();</span><br><span class="line">    man.name = <span class="string">"snail"</span>;</span><br><span class="line">    man.age = <span class="number">30</span>;</span><br><span class="line">    man.money = <span class="number">10000</span>;</span><br><span class="line"></span><br><span class="line">    Woman woman = <span class="keyword">new</span> Woman();</span><br><span class="line">    woman.name = <span class="string">"lucy"</span>;</span><br><span class="line">    </span><br><span class="line">    man.marry(woman);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//来了一个小偷</span></span><br><span class="line">    Thief thief = <span class="keyword">new</span> Thief();</span><br><span class="line">    thief.stealMoney(man);</span><br><span class="line">    thief.stealWife(man);</span><br><span class="line"></span><br><span class="line">    System.out.println(man.name + <span class="string">"的妻子"</span> + man.wife.name); <span class="comment">//snail的妻子：凤姐</span></span><br><span class="line">    System.out.println(man.name + <span class="string">"的钱："</span> + man.money); <span class="comment">//snail的钱：0.0</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>现在傻眼了，钱和老婆都成别人的了，自己还莫名其妙的和凤姐结了婚…<p></p><p>这时，你觉得是时候改变一下了！！！</p><h1 id="2-封装来报到"><a href="#2-封装来报到" class="headerlink" title="2. 封装来报到"></a>2. 封装来报到</h1><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><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></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">PackagePerson</span></span>&#123;</span><br><span class="line">  <span class="keyword">private</span> String name;</span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">int</span> age;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 省略get和set方法</span></span><br><span class="line">&#125;</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">PackageMan</span> <span class="keyword">extends</span> <span class="title">PackagePerson</span></span>&#123;</span><br><span class="line">  <span class="keyword">private</span> PackageWoman wife;</span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">double</span> money;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="title">PackageMan</span><span class="params">(String name, <span class="keyword">double</span> money)</span></span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.setName(name);</span><br><span class="line">    <span class="keyword">this</span>.money = money;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">marry</span><span class="params">(PackageWoman woman)</span></span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.wife = woman;</span><br><span class="line">    woman.marry(<span class="keyword">this</span>);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> PackageWoman <span class="title">getWife</span><span class="params">()</span></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> wife;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">double</span> <span class="title">getMoney</span><span class="params">()</span></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> money;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">PackageWoman</span> <span class="keyword">extends</span> <span class="title">PackagePerson</span></span>&#123;</span><br><span class="line">  <span class="keyword">private</span> PackageMan husband;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="title">PackageWoman</span><span class="params">(String name)</span></span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.setName(name);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">marry</span><span class="params">(PackageMan man)</span></span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.husband = husband;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> PackageMan <span class="title">getHusband</span><span class="params">()</span></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> husband;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestPackage</span></span>&#123;</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">    PackageMan man = <span class="keyword">new</span> PackageMan(<span class="string">"snail"</span>, <span class="number">10000</span>);</span><br><span class="line">    PackageWoman woman = <span class="keyword">new</span> PackageWoman(<span class="string">"lucy"</span>);</span><br><span class="line">    </span><br><span class="line">    man.marry(woman);</span><br><span class="line"></span><br><span class="line">    System.out.println(man.getName() + <span class="string">"的妻子"</span> + man.getWife().getName()); <span class="comment">//snail的妻子：lucy</span></span><br><span class="line">    System.out.println(man.getName() + <span class="string">"的钱："</span> + man.getMoney()); <span class="comment">//snail的钱：10000.0</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>上面的代码看起来除了长了点，没什么其他问题。这时候小偷已经不能偷我们的钱和老婆了，钱和老婆都被保护了起来，以至于我们自己想设置和更换都不行了，这明显不太科学…</p><h1 id="3-封装厉害的地方"><a href="#3-封装厉害的地方" class="headerlink" title="3. 封装厉害的地方"></a>3. 封装厉害的地方</h1><p>如何解决上面的问题呢？私有化外部访问不到，自己也没法改数据，提供了set方法又会让所有人都能改，和不私有设计没什么区别，好纠结。</p><p>Wait，这里说的“所有人”真的是所有人吗？</p><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><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setMoney</span><span class="params">(PackageMan man, <span class="keyword">double</span> money)</span></span>&#123;</span><br><span class="line">  <span class="keyword">if</span>(man == <span class="keyword">this</span>)&#123;</span><br><span class="line">    <span class="keyword">this</span>.money = money;</span><br><span class="line">  &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">    System.out.println(<span class="string">"喂，110吗？"</span> + man.getName() + <span class="string">" 抢钱！"</span>);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestPackage</span></span>&#123;</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">    PackageMan man = <span class="keyword">new</span> PackageMan(<span class="string">"snail"</span>, <span class="number">10000</span>);</span><br><span class="line">    PackageMan man1 = <span class="keyword">new</span> PackageMan(<span class="string">"thief"</span>, <span class="number">10000</span>);</span><br><span class="line"></span><br><span class="line">    man.setMoney(man, <span class="number">20000</span>);</span><br><span class="line">    System.out.println(man.getName() + <span class="string">"的钱："</span> + man.getMoney()); <span class="comment">//snail的钱：20000.0</span></span><br><span class="line"></span><br><span class="line">    man.setMoney(man1, <span class="number">0</span>); <span class="comment">//喂，110吗？thief 抢钱！</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>这样就只有自己可以修改了，别人不可以。</p><p>但是你老婆不满意了，凭什么只有你自己可以改？我也想改！</p><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></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">setMoney</span><span class="params">(Object obj, <span class="keyword">double</span> money)</span></span>&#123;</span><br><span class="line">  <span class="keyword">if</span>(obj == <span class="keyword">this</span> || obj == <span class="keyword">this</span>.wife)&#123;</span><br><span class="line">    <span class="keyword">this</span>.money = money;</span><br><span class="line">  &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">    System.out.println(<span class="string">"喂，110吗？有人抢钱！"</span>);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><h1 id="4-总结一下"><a href="#4-总结一下" class="headerlink" title="4. 总结一下"></a>4. 总结一下</h1><p>以上就是对面向对象中的封装的理解，封装不仅仅只是 private + getter and setter。使用封装可以对setter进行更深层次的定制，我们可以对可以执行的setter方法的对象做规定，也可以对数据操作要求，还可以做类型转换等一系列可以想到的。</p><p>使用封装不仅仅是安全，更可以简化操作。不要觉得用了封装多了好多代码，看起来乱糟糟的。如果你写一个大系统，一开始你可能这样定义属性的<br></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">int</span> age;</span><br></pre></td></tr></table></figure><br>你的程序里大概有100处这样的语句：<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">p.age = <span class="number">10</span>;</span><br></pre></td></tr></table></figure><br>这个时候，突然有需求要求把数据类型变了，改成：<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> String age;</span><br></pre></td></tr></table></figure><br>那么重构代码是不是要把那100处数据都加个双引号呢？是不是特别麻烦？<p></p><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></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">setAge</span><span class="params">(<span class="keyword">int</span> age)</span></span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.age = String.valueOf(age);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>是不是简化了操作？<p></p><p>这里只是举个栗子，实际开发中也不会出现改变数据类型这么操蛋的事…</p><p>封装还有一个好处就是模块化。当你参与一个很多人实现的大型系统中，不可能知道所有的类是怎样实现的。只需要知道这个类提供了哪些方法，需要传入什么数据，能得到什么样的结果。至于怎么得到的，关我X事？</p><p>所以说，如果你写的代码还没用封装，改过来吧。不仅仅因为大家都在用，而是这确实可以给我们提供很大的便利。<br>封装的有以下四大好处：</p><ol><li>良好的封装能够减少耦合</li><li>类内部的结构可以自由修改</li><li>可以对成员进行更精确的控制</li><li>隐藏信息和实现细节</li></ol>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;/images/encapsulation_2.png&quot; alt=&quot;encapsulation&quot;&gt;&lt;/p&gt;&lt;p&gt;对于面向对象的特点，想必大家应该都可以倒背如流：封装，继承，多态。但很多人对这些特点的理解仅仅停留在表面，认为封装就是变量的私有化，然后对外开放接口，获取和设置值，而不知道为什么要这样做。&lt;br&gt;
    
    </summary>
    
    
      <category term="Java基础" scheme="http://blog.develophelper.com/categories/java%E5%9F%BA%E7%A1%80/"/>
    
    
      <category term="Java基础" scheme="http://blog.develophelper.com/tags/java%E5%9F%BA%E7%A1%80/"/>
    
  </entry>
  
  <entry>
    <title>java基础（一）面向对象</title>
    <link href="http://blog.develophelper.com/article/java-base-object-oriented.html"/>
    <id>http://blog.develophelper.com/article/java-base-object-oriented.html</id>
    <published>2018-06-01T14:13:32.000Z</published>
    <updated>2020-02-15T15:23:10.032Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/images/object-oriented-programming_1.jpg" alt="object-oriented-programming"></p><p>面向对象的思想面向过程的思想有着本质的区别的，对于面向过程的思维来说，我们分析解决问题时会将问题拆分成n个步骤，第一步先做什么，第二步再做什么；而对于面向对象的思维来说，<strong>会首先分析问题里面涉及到哪些类和对象，然后再分析这些类和对象应该具有哪些属性和方法，最后分析类与类之间的关系</strong>。<br><a id="more"></a><br>面向对象有一个非常重要的设计思维：合适的方法应该出现在合适的类里面。</p><h1 id="1-简单理解面向对象的思想"><a href="#1-简单理解面向对象的思想" class="headerlink" title="1. 简单理解面向对象的思想"></a>1. 简单理解面向对象的思想</h1><p>面对问题域：</p><p>不能</p><ul><li><del>考虑解决方案实现，第一步应该干什么，第二步应该干什么…</del></li></ul><p>应该</p><ul><li>先分析问题解决方案 涉及到的类和对象</li><li>然后分析各个类和对象对应的动静态属性</li><li>最后分析抽象对象和对象之间的关系</li></ul><h1 id="2-面向对象的设计思想"><a href="#2-面向对象的设计思想" class="headerlink" title="2. 面向对象的设计思想"></a>2. 面向对象的设计思想</h1><p>面向对象的基本思想是，从现实世界中可观存在的事物出发来构造软件系统，并在系统的构造中尽可能运用人类的自然思维方式。</p><p>面向对象更加强调运用人类在日常生活的逻辑思维中经常采用的思想方法与原则，如抽象、分类、继承、聚合、多态等。</p><p>人在思考的时候，首先眼睛里看到的是一个个对象。</p><h2 id="2-1-对象和类的概念"><a href="#2-1-对象和类的概念" class="headerlink" title="2.1 对象和类的概念"></a>2.1 对象和类的概念</h2><p>对象是用于计算机语言对问题域中事物的描述，对象通过“属性(attribute)”和“方法(method)”来分别对应事物所具有的静态属性和动态属性。</p><p>类是用于描述同一类对象的一个抽象的概念，类中定义了这一类对象所具有的静态属性和动态属性。</p><p>类可以看成一类对象的模板，对象可以看成该类的一个具体的实例。</p><p>举个栗子：</p><p>人的定义：能制造工具并能熟练使用工具进行劳动的高等动物。</p><p>给人下定义的过程，其实就是把人的相关特性抽象出来，如：</p><ol><li>能制造工具；</li><li>能熟练使用工具；</li><li>高等动物。</li></ol><p>人在这里叫做这一类事物的抽象，在你的脑子里有人的概念，可人的概念在脑子里到底是怎样的？ 首先人是高等动物，那么高等动物这个特征就是人的静态属性，其次，这个高等动物可以 制造工具并熟练使用工具，制造工具和熟练使用工具就是人的动态属性。反应到java的类上，就是一个是成员变量（静态属性），一个是方法（动态属性）。脑子里的人的概念其实是一类事物的抽象，这种抽象的东西我们就叫做类，椅子是类，桌子是类，交通工具也是类，而这一类事物的具体实例就是对象了。</p><h2 id="2-2-如何抽象出一个类"><a href="#2-2-如何抽象出一个类" class="headerlink" title="2.2 如何抽象出一个类"></a>2.2 如何抽象出一个类</h2><p>上文已经说明面向对象的设计思想就是对问题域特征进行抽象，抽象出一些类，那么该如何去做呢？首先需要明确的是，<strong>类具有静态属性和动态属性</strong>，那么反应到java类中的体现就是，<strong>成员变量和方法</strong>。</p><p>让我们来看一下职员的抽象：</p><p><img src="/images/object-oriented-programming_2.jpg" alt="object-oriented-programming"></p><p>首先分析下，职员有哪些属性？有姓名，年龄，目前薪资等属性；有哪些方法？显示姓名，显示年龄，修改姓名，领取工资。当然，所有的方法其实都可以让别人来做，但<strong>面向对象的设计思想就是方法应该出现在最合适的类里面</strong>，这里显然职员这个类自己最合适。</p><p>对于每个对象来说，它都有一些属性（成员变量），只不过对象间的取值不同而已。上图所描述的职员这个类实例化出两个职员，职员A和职员B，他们都有姓名、年龄和目前薪资这些属性，但具体数值却不一样，正因为这些变化，对象和对象之间才能区分开来。</p><p>如何区分类和对象？类是一类具有共同特征事物的抽象，而对象就是这个类下面的具体实例了。</p><h2 id="2-3-类（对象）之间的关系"><a href="#2-3-类（对象）之间的关系" class="headerlink" title="2.3 类（对象）之间的关系"></a>2.3 类（对象）之间的关系</h2><h3 id="2-3-1-关联关系"><a href="#2-3-1-关联关系" class="headerlink" title="2.3.1 关联关系"></a>2.3.1 关联关系</h3><p><img src="/images/object-oriented-programming_3.jpg" alt="object-oriented-programming"></p><p>类和类之间存在关系的，如学生和老师这两个类，老师可以教学生，学生可以向老师学习，这是他们之间的关系，而关系和关系之间也是不同，你和你老婆的关系和你和你其他女朋友的关系是不能混为一谈的。关系中<strong>最弱</strong>的一种关系就是关联关系了。关联关系反应到程序上往往是一个类的方法里面的参数是另一个类的具体对象，比如教授教学生，教哪个学生，教学是教授这个类的一个方法，某个研究生是研究生这个类里面的一个具体对象，这两个类或者说对象之间有关系，但是不是很紧密的关系。</p><h3 id="2-3-2-继承关系"><a href="#2-3-2-继承关系" class="headerlink" title="2.3.2 继承关系"></a>2.3.2 继承关系</h3><p><img src="/images/object-oriented-programming_4.jpg" alt="object-oriented-programming"></p><p>对应：一般和特殊</p><p>继承关系的前提：XX是一种XX。满足继承的前提就可以称之为继承关系，如游泳运动员是一种运动员，这点是满足的，那么游泳运动员就是从运动员继承过来的，游泳运动员和运动员之间就是一种继承关系。学生是一个人，学生从人继承，老师也是一个人，老师也从人继承，学生是一种老师，这点说不过去，所以学生和老师之间就不存在继承关系，因此做设计的时候分清继承关系很简单，那就是使得“XX是一种XX”说的过去。图中，运动员派生出了不同种类的运动员，包括游泳的，球类的，射击的。球类运动员再派生出足球的，篮球的，排球的，这就是一棵继承树，不过这棵树是比较理想的情况，只有一个根节点。但是实际中，继承关系不一定只从一个类继承，可能从多个类继承，比如说，孩子即继承父亲这个类，也继承母亲这个类。C++正是想封装这种关系，所以它存在多继承。</p><h3 id="2-3-3-聚合关系"><a href="#2-3-3-聚合关系" class="headerlink" title="2.3.3 聚合关系"></a>2.3.3 聚合关系</h3><p><img src="/images/object-oriented-programming_5.jpg" alt="object-oriented-programming"></p><p>对应：整体和部分</p><p>什么是聚合？聚合就是一个整体和部分的关系。我们是“XX是XX的一部分”，只要说的通，他就是聚合关系，队长是球队的一部分，队员也是球队的一部分，因此队长和球队，队员和球队都是聚合关系。脑袋是人的一部分，身体和胳膊也是人的一部分，因此脑袋、身体和胳膊与人都是聚合关系。聚合关系再分细一点就是<strong>聚集关系和组合关系</strong>，比如球队、队长、队员，这三者是聚集关系，假如这个队长既是足球队的队长，也是篮球队的队长，一个人分属两个不同的球队，这是可以的，球队和队长之间没有不可分割的关系，这就是<strong>聚集关系</strong>。而另一种<strong>组合关系</strong>，就是一种不可分割的关系，相对于一个人的脑袋不可能既属于你又属于别人，身体也一样不可能同时属于多个人。</p><h3 id="2-3-4-实现关系"><a href="#2-3-4-实现关系" class="headerlink" title="2.3.4 实现关系"></a>2.3.4 实现关系</h3><p><img src="/images/object-oriented-programming_6.jpg" alt="object-oriented-programming"></p><p>对于父类（抽象类）或接口来说，我觉得应该具有这样一个方法，但是不知道具体怎么实现，谁去实现，我的子类实现，这就是实现关系。而根据实现不同，具有不同的表现形态，这就是多态。</p><h3 id="2-3-5-多态"><a href="#2-3-5-多态" class="headerlink" title="2.3.5 多态"></a>2.3.5 多态</h3><p><img src="/images/object-oriented-programming_7.jpg" alt="object-oriented-programming"></p><h1 id="3-Java与面向对象"><a href="#3-Java与面向对象" class="headerlink" title="3. Java与面向对象"></a>3. Java与面向对象</h1><ul><li>对象是Java程序的核心，在Java程序中“万物皆对象”；</li><li>对象可以看成是静态属性（成员变量）和动态属性（方法）的封装体；</li><li>类是创建同一类型对象的“模板”，在类中定义了一个对象所应具有的成员变量和方法；</li><li>J2SDK提供了很多类供编程人员使用，编程人员也可以定义自己的类。</li></ul><p>对象和类是分不开的，必须首先定义类才能有对象，首先定义方法才能调用。对象是Java里面的核心，做任何东西你首先造出一个对象才能做。静态属性简称属性，也叫成员变量，以后说属性或者说成员变量他们指的都是同一回事。</p><p>整个类可以看作是静态的属性还有方法之间的一个综合。怎么抽象出一个类的概念，从两方面入手，一个是静态属性，即具有哪些成员变量，另一个就是动态属性，具有哪些方法。</p><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><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Person</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">private</span> String age;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 省略getter和setter方法</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">makeTools</span><span class="params">()</span></span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"我在制造工具"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">useTools</span><span class="params">()</span></span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"我在使用工具"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Man</span> <span class="keyword">extends</span> <span class="title">Person</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Woman</span> <span class="keyword">extends</span> <span class="title">Person</span> </span>&#123;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><h1 id="4-为什么使用面向对象编程？"><a href="#4-为什么使用面向对象编程？" class="headerlink" title="4. 为什么使用面向对象编程？"></a>4. 为什么使用面向对象编程？</h1><p>面向对象编程就是使用一组对象互相配合通过沟通一起完成特定的功能.做软件苦苦追求的一种境界是可重用性（reusable）,可扩展性。如果是面向过程，一般情况下属性和方法是分开的，没有关系的，这样复用起来很麻烦，而且局限在方法这个层次上，而面向对象则不同，它将属性和方法包装，可以综合起来复用，就是整个对象的复用。所以面向对象和面向过程相比，前者明显更容易让我们达到可重用性。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;/images/object-oriented-programming_1.jpg&quot; alt=&quot;object-oriented-programming&quot;&gt;&lt;/p&gt;&lt;p&gt;面向对象的思想面向过程的思想有着本质的区别的，对于面向过程的思维来说，我们分析解决问题时会将问题拆分成n个步骤，第一步先做什么，第二步再做什么；而对于面向对象的思维来说，&lt;strong&gt;会首先分析问题里面涉及到哪些类和对象，然后再分析这些类和对象应该具有哪些属性和方法，最后分析类与类之间的关系&lt;/strong&gt;。&lt;br&gt;
    
    </summary>
    
    
      <category term="Java基础" scheme="http://blog.develophelper.com/categories/java%E5%9F%BA%E7%A1%80/"/>
    
    
      <category term="Java基础" scheme="http://blog.develophelper.com/tags/java%E5%9F%BA%E7%A1%80/"/>
    
  </entry>
  
  <entry>
    <title>Consul服务治理</title>
    <link href="http://blog.develophelper.com/article/microservice-consul.html"/>
    <id>http://blog.develophelper.com/article/microservice-consul.html</id>
    <published>2018-05-09T09:53:00.000Z</published>
    <updated>2020-02-15T15:23:10.032Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/images/consul.jpg" alt="consul"></p><p>Consul是google开源的一个使用go语言开发的服务发现、配置管理中心服务。内置了服务注册与发现框架、分布一致性协议实现、健康检查、Key/Value存储、多数据中心方案，不再需要依赖其他工具（如ZooKeeper等）。服务部署简单，只有一个可运行的二进制的包。每个节点都需要运行agent，他有两种运行模式server和client。每个数据中心官方建议需要3或5个server节点以保证数据安全，同时保证server-leader的选举能够正确的进行。<br><a id="more"></a></p><ul><li><p>client</p><p>client表示Consul的client模式，就是客户端模式。是Consul节点的一种模式，这种模式下，所有注册到当前节点的服务会被转发到server，本身是不持久化这些信息。</p></li><li><p>server</p><p>server表示Consul的server模式，表明这个Consul是个server，这种模式下，功能和client都一样，唯一不同的是，它会把所有的信息持久化的本地，这样遇到故障，信息是可以被保留的。</p></li><li><p>server-leader</p><p>中间那个server下面有leader的字眼，表明这个server是它们的老大，它和其它server不一样的一点是，它需要负责同步注册的信息给其它的server，同时也要负责各个节点的健康监测。</p></li><li><p>raft</p><p>server节点之间的数据一致性保证，一致性协议使用的是raft，而zookeeper用的paxos，etcd采用的也是taft。</p></li><li><p>服务发现协议</p><p>Consul采用http和dns协议，etcd只支持http</p></li><li><p>服务注册</p><p>Consul支持两种方式实现服务注册，一种是通过Consul的服务注册http API，由服务自己调用API实现注册，另一种方式是通过json个是的配置文件实现注册，将需要注册的服务以json格式的配置文件给出。Consul官方建议使用第二种方式。</p></li><li><p>服务发现</p><p>Consul支持两种方式实现服务发现，一种是通过http API来查询有哪些服务，另外一种是通过Consul agent 自带的DNS（8600端口），域名是以NAME.service.consul的形式给出，NAME即在定义的服务配置文件中，服务的名称。DNS方式可以通过check的方式检查服务。</p></li><li><p>服务间的通信协议</p><p>Consul使用gossip协议管理成员关系、广播消息到整个集群，他有两个gossip pool（LAN pool和WAN pool），LAN pool是同一个数据中心内部通信的，WAN pool是多个数据中心通信的，LAN pool有多个，WAN pool只有一个。</p></li></ul><h2 id="1-安装"><a href="#1-安装" class="headerlink" title="1. 安装"></a>1. 安装</h2><p>首先去官网现在合适的consul包：<a href="https://www.consul.io/downloads.html" target="_blank" rel="external nofollow noopener noreferrer">https://www.consul.io/downloads.html</a><br>安装直接下载zip包，解压后只有一个可执行的文件consul，将consul添加到系统的环境变量里面。<br></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#unzip consul_1.0.2_linux_amd64.zip</span></span><br><span class="line"><span class="comment">#cp -a consul  /usr/bin</span></span><br><span class="line"><span class="comment">#consul</span></span><br></pre></td></tr></table></figure><br><img src="/images/consul_1.png" alt="1200972-20180104111330034-1408661682"><p></p><p>出现上面的内容证明安装成功。</p><h2 id="2-启动"><a href="#2-启动" class="headerlink" title="2. 启动"></a>2. 启动</h2><p>consul必须启动agent才能使用，有两种启动模式server和client，还有一个官方自带的ui。server用与持久化服务信息，集群官方建议3或5个节点。client只用与于server交互。ui可以查看集群情况的。</p><p><strong>Server</strong></p><p>cn1：<br></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#consul agent -bootstrap-expect 2 -server -data-dir /data/consul0 -node=cn1 -bind=192.168.1.202 -config-dir /etc/consul.d -enable-script-checks=true -datacenter=dc1</span></span><br></pre></td></tr></table></figure><p></p><p>cn2:<br></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#consul agent -server  -data-dir /data/consul0 -node=cn2 -bind=192.168.1.201 -config-dir /etc/consul.d -enable-script-checks=true -datacenter=dc1 -join 192.168.1.202</span></span><br></pre></td></tr></table></figure><p></p><p>cn3:<br></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#consul agent -server -data-dir /data/consul0 -node=cn3 -bind=192.168.1.200 -config-dir /etc/consul.d -enable-script-checks=true -datacenter=dc1 -join 192.168.1.202</span></span><br></pre></td></tr></table></figure><p></p><blockquote><p>参数解释：</p><ul><li>-bootstrap-expect： 集群期望的节点数，只有节点数量达到这个值才会选举leader。</li><li>-server： 运行在server模式</li><li>-data-dir：指定数据目录，其他的节点对于这个目录必须有读的权限</li><li>-node：指定节点的名称</li><li>-bind：为该节点绑定一个地址</li><li>-config-dir：指定配置文件，定义服务的，默认所有一.json结尾的文件都会读</li><li>-enable-script-checks=true：设置检查服务为可用</li><li>-datacenter： 数据中心没名称，</li><li>-join：加入到已有的集群中</li></ul></blockquote><p><strong>Client</strong><br></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#consul agent   -data-dir /data/consul0 -node=cn4 -bind=192.168.1.199 -config-dir /etc/consul.d -enable-script-checks=true  -datacenter=dc1  -join 192.168.1.202</span></span><br></pre></td></tr></table></figure><br>client节点可以有多个，自己根据服务指定即可。<p></p><p><strong>UI</strong><br></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#consul agent  -ui  -data-dir /data/consul0 -node=cn4 -bind=192.168.1.198  -client 192.168.1.198   -config-dir /etc/consul.d -enable-script-checks=true  -datacenter=dc1  -join 192.168.1.202</span></span><br></pre></td></tr></table></figure><p></p><blockquote><ul><li>-ui：使用自带的ui，</li><li>-ui-dir：指定ui的目录，使用自己定义的ui</li><li>-client：指定web ui、的监听地址，默认127.0.0.1只能本机访问。</li></ul></blockquote><p>集群创建完成后，可以使用一些常用的命令检查集群的状态：<br></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#consul  info</span></span><br></pre></td></tr></table></figure><br>可以在raft：stat看到此节点的状态是fllower或者leader<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#consul members</span></span><br><span class="line"></span><br><span class="line">Node Address Status Type Build Protocol DC Segment</span><br><span class="line">cn1 192.168.1.202:8301 alive server 1.0.2 2 dc1 &lt;all&gt;</span><br><span class="line">cn2 192.168.1.201:8301 alive server 1.0.2 2 dc1 &lt;all&gt;</span><br><span class="line">cn3 192.168.1.200:8301 alive client 1.0.2 2 dc1 &lt;default&gt;</span><br></pre></td></tr></table></figure><p></p><p>新加入一个节点有几种方式:</p><ol><li><p>这种方式，重启后不会自动加入集群</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#consul  join  192.168.1.202</span></span><br></pre></td></tr></table></figure></li><li><p>在启动的时候使用-join指定一个集群</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#consul agent  -ui  -data-dir /data/consul0 -node=cn4 -bind=192.168.1.198 -config-dir /etc/consul.d -enable-script-checks=true  -datacenter=dc1  -join 192.168.1.202</span></span><br></pre></td></tr></table></figure></li><li><p>使用-startjoin或-rejoin</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#consul agent  -ui  -data-dir /data/consul0 -node=cn4 -bind=192.168.1.198 -config-dir /etc/consul.d -enable-script-checks=true  -datacenter=dc1  -rejoin</span></span><br></pre></td></tr></table></figure></li></ol><p>访问ui:<br><a href="http://192.168.1.198:8500/ui" target="_blank" rel="external nofollow noopener noreferrer">http://192.168.1.198:8500/ui</a></p><blockquote><p>端口：<br>8300：consul agent服务relplaction、rpc（client-server）<br>8301：lan gossip<br>8302：wan gossip<br>8500：http api端口<br>8600：DNS服务端口</p></blockquote><h2 id="3-服务注册"><a href="#3-服务注册" class="headerlink" title="3. 服务注册"></a>3. 服务注册</h2><p>采用的是配置文件的方式，（官方推荐）首先创建一个目录用于存放定义服务的配置文件<br></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#mkdir /etc/consul.d/</span></span><br></pre></td></tr></table></figure><br>启动服务的时候要使用-config-dir 参数指定。<p></p><p>下面给出一个服务定义：<br></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#cat web.json</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">"service"</span>: &#123;</span><br><span class="line">    <span class="string">"name"</span>: <span class="string">"web"</span>,</span><br><span class="line">    <span class="string">"tags"</span>: [<span class="string">"rails"</span>],</span><br><span class="line">    <span class="string">"port"</span>: 80,</span><br><span class="line">    <span class="string">"check"</span>: &#123;</span><br><span class="line">      <span class="string">"name"</span>: <span class="string">"ping"</span>,</span><br><span class="line">      <span class="string">"script"</span>: <span class="string">"curl -s localhost:80"</span>,</span><br><span class="line">      <span class="string">"interval"</span>: <span class="string">"3s"</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><br>启动后就可以在没有运行web服务的机器上面执行DNS查询：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># dig @127.0.0.1 -p 8600 web.service.consul SRV</span></span><br><span class="line"></span><br><span class="line">;; ANSWER SECTION:</span><br><span class="line">web.service.consul.0INSRV1 1 80 cn2.node.dc1.consul.</span><br><span class="line">web.service.consul.0INSRV1 1 80 cn3.node.dc1.consul.</span><br><span class="line"></span><br><span class="line">;; ADDITIONAL SECTION:</span><br><span class="line">cn2.node.dc1.consul.0INA192.168.1.201</span><br><span class="line">cn2.node.dc1.consul.0INTXT<span class="string">"consul-network-segment="</span></span><br><span class="line">cn3.node.dc1.consul.0INA192.168.1.200</span><br><span class="line">cn3.node.dc1.consul.0INTXT<span class="string">"consul-network-segment="</span></span><br><span class="line"></span><br><span class="line">;; Query time: 17 msec</span><br><span class="line">;; SERVER: 127.0.0.1<span class="comment">#8600(127.0.0.1)</span></span><br><span class="line">;; WHEN: 四 1月 04 14:39:32 CST 2018</span><br><span class="line">;; MSG SIZE rcvd: 229</span><br></pre></td></tr></table></figure><br>可以看到服务已经注册到集群里面了。<br>使用dns查询，默认域名格式NAME.service.consul，NAME就是web.json里面定义的service的name。可以自己指定域和端口：-domain、-dns-port 53<p></p><h2 id="4-健康检查"><a href="#4-健康检查" class="headerlink" title="4. 健康检查"></a>4. 健康检查</h2><p>check使用来做服务的健康检查的，可以拥有多个，也可以不使用支持多种方式检查。check必须是script或者TTL类型的，如果是script类型则script和interval变量必须被提供，如果是TTL类型则ttl变量必须被提供。script是consul主动去检查服务的健康状况，ttl是服务主动向consul报告自己的状况。</p><ul><li><p>script check</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="string">"check"</span>: &#123;</span><br><span class="line">    <span class="string">"id"</span>: mutil - memory,</span><br><span class="line">    <span class="string">"name"</span>: <span class="string">"memory utilization"</span>,</span><br><span class="line">    <span class="string">"tags"</span>: [<span class="string">"system"</span>],</span><br><span class="line">    <span class="string">"script"</span>: <span class="string">"/etc/init.d/check_memory.py"</span>,</span><br><span class="line">    <span class="string">"interval"</span>: <span class="string">"10s"</span>,</span><br><span class="line">    <span class="string">"timeout"</span>: <span class="string">"1s"</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>http check</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="string">"check"</span>: &#123;</span><br><span class="line">    <span class="string">"id"</span>: <span class="string">"api"</span>,</span><br><span class="line">    <span class="string">"name"</span>: <span class="string">"HTTP API  500"</span>,</span><br><span class="line">    <span class="string">"http"</span>: <span class="string">"http://loclhost:500/health"</span>,</span><br><span class="line">    <span class="string">"interval"</span>: <span class="string">"10s"</span>,</span><br><span class="line">    <span class="string">"timeout"</span>: <span class="string">"1s"</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>tcp check</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="string">"check"</span>: &#123;</span><br><span class="line">    <span class="string">"id"</span>: <span class="string">"ssh"</span>,</span><br><span class="line">    <span class="string">"name"</span>: <span class="string">"ssh TCP 26622"</span>,</span><br><span class="line">    <span class="string">"tcp"</span>: <span class="string">"localhost:26622"</span>,</span><br><span class="line">    <span class="string">"interval"</span>: <span class="string">"10s"</span>,</span><br><span class="line">    <span class="string">"timeout"</span>: <span class="string">"1s"</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>ttl check</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="string">"check"</span>: &#123;</span><br><span class="line">    <span class="string">"id"</span>: <span class="string">"web-app"</span>,</span><br><span class="line">    <span class="string">"name"</span>: <span class="string">"Web APP status"</span>,</span><br><span class="line">    <span class="string">"notes"</span>: <span class="string">"Web APP does a curl  internally every 10 seconds"</span>,</span><br><span class="line">    <span class="string">"ttl"</span>: <span class="string">"30s"</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><p>全文完</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;/images/consul.jpg&quot; alt=&quot;consul&quot;&gt;&lt;/p&gt;&lt;p&gt;Consul是google开源的一个使用go语言开发的服务发现、配置管理中心服务。内置了服务注册与发现框架、分布一致性协议实现、健康检查、Key/Value存储、多数据中心方案，不再需要依赖其他工具（如ZooKeeper等）。服务部署简单，只有一个可运行的二进制的包。每个节点都需要运行agent，他有两种运行模式server和client。每个数据中心官方建议需要3或5个server节点以保证数据安全，同时保证server-leader的选举能够正确的进行。&lt;br&gt;
    
    </summary>
    
    
      <category term="服务治理" scheme="http://blog.develophelper.com/categories/%E6%9C%8D%E5%8A%A1%E6%B2%BB%E7%90%86/"/>
    
    
      <category term="服务治理" scheme="http://blog.develophelper.com/tags/%E6%9C%8D%E5%8A%A1%E6%B2%BB%E7%90%86/"/>
    
  </entry>
  
  <entry>
    <title>高性能js库Lodash</title>
    <link href="http://blog.develophelper.com/article/javascript-library-lodash.html"/>
    <id>http://blog.develophelper.com/article/javascript-library-lodash.html</id>
    <published>2018-05-08T03:09:00.000Z</published>
    <updated>2020-02-15T15:23:10.032Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/images/lodash_0.jpg" alt="timg"></p><p>有几年开发经验的工程师，往往都会有自己的一套工具库，称为utils、helpers等等，这套库一方面是自己的技术积累，另一方面也是对某项技术的扩展，领先于技术规范的制订和实现。<br>Lodash就是这样的一套工具库，它内部封装了诸多对字符串、数组、对象等常见数据类型的处理函数，其中部分是目前ECMAScript尚未制订的规范，但同时被业界所认可的辅助函数。而且每天使用npm安装Lodash的数量在百万级以上，这在一定程度上证明了其代码的健壮性，值得我们在项目中一试。<br><a id="more"></a></p><h2 id="1-模块组成"><a href="#1-模块组成" class="headerlink" title="1. 模块组成"></a>1. 模块组成</h2><ul><li>Lodash听得辅助函数主要分为以下几类，函数列表和用法实力请查看Lodash的官方文档：</li><li>Array， 适合于数组类型，比如填充数据、查找元素、数组分片等操作</li><li>Collocation， 适用于数组和对象类型，部分适用于字符串，比如分组、查找、过滤等操作</li><li>Function， 适用于函数类型，比如节流、延迟、缓存、设置钩子等操作</li><li>Lang， 普遍适用于各种类型，常用于执行类型判断和类型转换</li><li>Math， 使用与数值类型，常用于执行数学运算</li><li>Number， 适用于生成随机数，比较数值与数值区间的关系</li><li>Object， 适用于对象类型，常用于对象的创建、扩展、类型转换、检索、集合等操作</li><li>Seq， 常用于创建链式调用，提高执行性能（惰性计算）</li><li>String， 适用于字符串类型</li><li>lodash/fp 模块提供了更接近函数式编程的开发方法，其内部的函数经过包装，具有immutable、auto-curried、iteratee-first、data-last（官方介绍）等特点。</li><li>Fixed Arity，固化参数个数，便于柯里化</li><li>Rearragned Arguments， 重新调整参数位置，便于函数之间的聚合</li><li>Capped Iteratee Argument， 封装Iteratee参数</li></ul><h2 id="1-性能"><a href="#1-性能" class="headerlink" title="1. 性能"></a>1. 性能</h2><p>在 Filip Zawada的文章<a href="https://link.jianshu.com/?t=http://filimanjaro.com/blog/2014/introducing-lazy-evaluation/" target="_blank" rel="external nofollow noopener noreferrer">《How to Speed Up Lo-Dash ×100? Introducing Lazy Evaluation》</a>中提到了Lodash提高执行速度的思路，主要有三点： Lazy Evaluation、Pipelining和Deferred Execution。下面两张图来自Filip的博客：</p><p><img src="/images/lodash_1.gif" alt="976007-20180123132259975-836139498"></p><p>假设有如上图所示的问题： 从若干个求中取出三个面值小于10的球。第一步是从所有的求中取出所有面值小于10的球，第二部是从上一步的结果中去三个球。</p><p><img src="/images/lodash_2.gif" alt="976007-20180123132320240-161059187"></p><p>上图是另一个解决方案，如果一个球能够通过第一步，那么就继续执行第二步，直至结束然后测试下一个球。。。当我们取到三个球之后就中断整个循环。Filip称这是Lazy Evaluation Algorithm， 就个人理解这并不全面，他后续提到的Pipelining（管道计算），再加上一个中断循环执行的算法应该更符合这里的图示。</p><p>此外，使用Lodash的链式调用时，只有现实或隐式调用 .value 方法才会对链式调用的整个操作进行取值，这种不在声明时立即求值，而在使用时进行求职的方式，是Lazy Evaluation最大的特点。</p><h2 id="3-九个实例"><a href="#3-九个实例" class="headerlink" title="3. 九个实例"></a>3. 九个实例</h2><p>收益于Lodash的普及程度，使用它可以提高很多人开发时于都代码的效率，减少彼此之间的误解（Loss of Consciousness）。在<a href="https://link.jianshu.com/?t=http://colintoh.com/blog/lodash-10-javascript-utility-functions-stop-rewriting" target="_blank" rel="external nofollow noopener noreferrer">《Lodash: 10 Javascript Utility Functions That You Should Probably Stop Rewriting》</a>一文中，作者列举了多个常用的Lodash函数，实例演示了使用Lodash的技巧。</p><ol><li>N次循环</li></ol><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 1. Basic for loop.</span></span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; <span class="number">5</span>; i++)&#123;</span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. Using Array's join and split methods</span></span><br><span class="line"><span class="built_in">Array</span>.apply(<span class="literal">null</span>, <span class="built_in">Array</span>(<span class="number">5</span>)).forEach(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Lodash</span></span><br><span class="line">_.times(<span class="number">5</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></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>for 语句是执行虚幻的不二选择，Array.apply也可以模拟循环，但在上面代码的使用场景下，_.tiems()的解决方法更加简洁和易于理解。</p><ol><li><p>深层查找属性值</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Fetch the name of the first pet from each owner</span></span><br><span class="line"><span class="keyword">var</span> ownerArr = [&#123;</span><br><span class="line">    <span class="string">"owner"</span>: <span class="string">"Colin"</span>,</span><br><span class="line">    <span class="string">"pets"</span>: [&#123;<span class="string">"name"</span>: <span class="string">"dog1"</span>&#125;, &#123;<span class="string">"name"</span>: <span class="string">"dog2"</span>&#125;]</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">"owner"</span>: <span class="string">"John"</span>,</span><br><span class="line">    <span class="string">"pets"</span>: [&#123;<span class="string">"name"</span>: <span class="string">"dog3"</span>&#125;, &#123;<span class="string">"name"</span>: <span class="string">"dog4"</span>&#125;]</span><br><span class="line">&#125;];</span><br><span class="line"></span><br><span class="line"><span class="comment">// Array's map method.</span></span><br><span class="line">ownerArr.map(<span class="function"><span class="keyword">function</span>(<span class="params">owner</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> owner.pets[<span class="number">0</span>].name;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Lodash</span></span><br><span class="line">_.map(ownerArr, <span class="string">"pets[0].name"</span>);</span><br></pre></td></tr></table></figure><p>_.map 方法是对原生 map 方法的改进，其中使用 pets[0].name 字符串对嵌套数据取值的方式简化了很多冗余的代码，非常类似使用jQuery选择DOM节点 ul&gt;li&gt;a , 对于前端开发者来说有种久违的亲切感。</p></li><li><p>个性化数组</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Array's map method.</span></span><br><span class="line"><span class="built_in">Array</span>.apply(<span class="literal">null</span>, <span class="built_in">Array</span>(<span class="number">6</span>)).map(<span class="function"><span class="keyword">function</span>(<span class="params">item, index</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">"ball_"</span> + index; </span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Lodash</span></span><br><span class="line">_.times(<span class="number">6</span>, _.uniqueId.bind(<span class="literal">null</span>, <span class="string">'ball_'</span>));</span><br><span class="line"></span><br><span class="line"><span class="comment">// Lodash</span></span><br><span class="line">_.times(<span class="number">6</span>, _.partial(_.uniqueId, <span class="string">'ball_'</span>));</span><br><span class="line"><span class="comment">// eg. [ball_0, ball_1, ball_2, ball_3, ball_4, ball_6]</span></span><br></pre></td></tr></table></figure><p>在上面的代码中，我们要创建一个初始值不同、长度为6的数组，其中 <em>.uniqueId 方法用于生成独一无二的标示符（递增的数字，在程序运行期间保持独一无二）， </em>.partial 方法是对 bind 的封装。</p></li><li><p>深拷贝</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> objA = &#123;</span><br><span class="line">    <span class="string">"name"</span>: <span class="string">"colin"</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 常用的方法一般会比较长，循环对象等</span></span><br><span class="line"><span class="comment">// http://stackoverflow.com/questions/4459928/how-to-deep-clone-in-javascript</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Lodash</span></span><br><span class="line"><span class="keyword">var</span> objB = _.cloneDeep(objA);</span><br><span class="line">objB === objA <span class="comment">// false</span></span><br></pre></td></tr></table></figure><p>JavaScript 没有直接提供深拷贝的函数，但是我们可以用其他杉树来模拟，比如 JSON.parse(JSON.stringify(objectToClone)), 但这种方法要求对象中的属性值不能是函数。Lodash 中的 _.cloneDeep 函数封装了深拷贝的逻辑，用起来更加简洁。</p></li><li><p>随机数</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Native utility method</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getRandomNumber</span>(<span class="params">min, max</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">Math</span>.floor(<span class="built_in">Math</span>.random() * (max - min + <span class="number">1</span>)) + min;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">getRandomNumber(<span class="number">15</span>, <span class="number">20</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Lodash</span></span><br><span class="line">_.random(<span class="number">15</span>, <span class="number">20</span>);</span><br></pre></td></tr></table></figure><p>Lodash 的随机数生成函数更贴近实际开发，ECMAScript 的随机数生成函数式底层必备的接口，两者都不可获取。此外，使用 _.random(15, 20, true) 还可以在15到20之间生成随机的浮点数。</p></li><li><p>对象扩展</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><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">// Adding extend function to Object.prototype</span></span><br><span class="line"><span class="built_in">Object</span>.prototype.extend = <span class="function"><span class="keyword">function</span>(<span class="params">obj</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> i <span class="keyword">in</span> obj) &#123;</span><br><span class="line">        <span class="keyword">if</span> (obj.hasOwnProperty(i)) &#123;</span><br><span class="line">            <span class="keyword">this</span>[i] = obj[i];</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> objA = &#123;<span class="string">"name"</span>: <span class="string">"colin"</span>, <span class="string">"car"</span>: <span class="string">"suzuki"</span>&#125;;</span><br><span class="line"><span class="keyword">var</span> objB = &#123;<span class="string">"name"</span>: <span class="string">"james"</span>, <span class="string">"age"</span>: <span class="number">17</span>&#125;;</span><br><span class="line"></span><br><span class="line">objA.extend(objB);</span><br><span class="line">objA; <span class="comment">// &#123;"name": "james", "age": 17, "car": "suzuki"&#125;;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Lodash</span></span><br><span class="line">_.assign(objA, ojbB);</span><br></pre></td></tr></table></figure><p>_.assign 是浅拷贝， 和ES6新增的 Object.assign 函数功能一致（建议优先使用Object.assign）。</p></li><li><p>筛选属性</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Native method: Remove an array of keys from object</span></span><br><span class="line"><span class="built_in">Object</span>.prototype.remove = <span class="function"><span class="keyword">function</span>(<span class="params">arr</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> that = <span class="keyword">this</span>;</span><br><span class="line">    arr.forEach(<span class="function"><span class="keyword">function</span>(<span class="params">key</span>)</span>&#123;</span><br><span class="line">        <span class="keyword">delete</span>(<span class="keyword">this</span>[key]);</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> objA = &#123;<span class="string">"name"</span>: <span class="string">"colin"</span>, <span class="string">"car"</span>: <span class="string">"suzuki"</span>, <span class="string">"age"</span>: <span class="number">17</span>&#125;;</span><br><span class="line"></span><br><span class="line">objA.remove([<span class="string">'car'</span>, <span class="string">'age'</span>]);</span><br><span class="line">objA; <span class="comment">// &#123;"name": "colin"&#125;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Lodash</span></span><br><span class="line">objA = _.omit(objA, [<span class="string">'car'</span>, <span class="string">'age'</span>]);</span><br><span class="line"><span class="comment">// =&gt; &#123;"name": "colin"&#125;</span></span><br><span class="line"></span><br><span class="line">objA = _.omit(objA, <span class="string">"car"</span>);</span><br><span class="line"><span class="comment">// =&gt; &#123;"name": "colin", "age": 17&#125;</span></span><br><span class="line"></span><br><span class="line">objA = _.omit(objA, _.isNumber);</span><br><span class="line"><span class="comment">// =&gt; &#123;"name": "colin", "car": "suzuki"&#125;;</span></span><br></pre></td></tr></table></figure><p>大多数情况下，Lodash所提供的辅助函数都会比原声的函数更贴近开发需求。在上面的代码中，开发者可以使用数组、字符串以及函数的方式筛选对象的属性，并且最终会返回一个新的对象，中间执行筛选时不会对旧对象产生影响。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Native method: Returning a new object with selected properties</span></span><br><span class="line"><span class="built_in">Object</span>.prototype.pick = <span class="function"><span class="keyword">function</span>(<span class="params">arr</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> _this = <span class="keyword">this</span>;</span><br><span class="line">    <span class="keyword">var</span> obj = &#123;&#125;;</span><br><span class="line">    arr.forEach(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        obj[key] = _this[key];</span><br><span class="line">    &#125;);</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> obj;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> objA = &#123;<span class="string">"name"</span>: <span class="string">"colin"</span>, <span class="string">"car"</span>: <span class="string">"suzuki"</span>, <span class="string">"age"</span>: <span class="number">17</span>&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> objB = objA.pick([<span class="string">'car'</span>, <span class="string">'age'</span>]);</span><br><span class="line"><span class="comment">// =&gt; &#123;"car": "suzuki", "age": 17&#125;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Lodash</span></span><br><span class="line"><span class="keyword">var</span> objB = _.pick(objA, [<span class="string">'car'</span>, <span class="string">'age'</span>]);</span><br><span class="line"><span class="comment">// =&gt; &#123;"car": "suzuki", "age":17&#125;</span></span><br></pre></td></tr></table></figure><p><em>.pick 是 </em>.omit 的相反操作，用于从其他对象中挑选属性生成新的对象。</p></li><li><p>随机元素</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> luckDraw = [<span class="string">"Colin"</span>, <span class="string">"John"</span>, <span class="string">"James"</span>, <span class="string">"Lily"</span>, <span class="string">"Mary"</span>];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">pickRandomPerson</span>(<span class="params">luckyDraw</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">var</span> index = <span class="built_in">Math</span>.floor(<span class="built_in">Math</span>.random() * (luckyDraw.length - <span class="number">1</span>));</span><br><span class="line">    <span class="keyword">return</span> luckyDraw[index];</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">pickRandomPerson(luckyDraw); <span class="comment">//John</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Lodash</span></span><br><span class="line">_.sample(luckyDraw); <span class="comment">// Colin</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Lodash - Getting 2 random item</span></span><br><span class="line">_.sample(luckyDraw, <span class="number">2</span>); <span class="comment">// ['John', 'Lily']</span></span><br></pre></td></tr></table></figure><p>_.sample 支持随机挑选多个元素并返回新的数组。</p></li><li><p>针对 JSON.parse 的错误处理</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Using try-catch to handle the JSON.parse error</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">parse</span>(<span class="params">str</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">JSON</span>.parse(str);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">catch</span>(e) &#123;</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">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// With Lodash</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">parseLodash</span>(<span class="params">str</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> _.attempt(<span class="built_in">JSON</span>.parse.bind(<span class="literal">null</span>, str));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">parse(<span class="string">'a'</span>);</span><br><span class="line"><span class="comment">// =&gt; false</span></span><br><span class="line">parseLodash(<span class="string">'a'</span>);</span><br><span class="line"><span class="comment">// =&gt; Return an error object</span></span><br><span class="line"></span><br><span class="line">parse(<span class="string">'&#123;"name": "colin"&#125;'</span>);</span><br><span class="line"><span class="comment">// =&gt; Return &#123;"name": "colin"&#125;</span></span><br><span class="line">parseLodash(<span class="string">'&#123;"name": "colin"&#125;'</span>);</span><br><span class="line"><span class="comment">// =&gt; Return &#123;"name": "colin"&#125;</span></span><br></pre></td></tr></table></figure><p>如果你在使用 JSON.parse 时没有预置错误处理，那么它很有可能会成为一个定时炸弹，我们不应该默认接收的JSON对象都是有效的。 try-catch 是常见的错误处理方式，如果项目中使用Lodash，那么可以使用 _.attmpt 替代 try-catch 的方式，当解析JSON出错时，该方法会返回一个 Error 对象。</p></li></ol><blockquote><p>随着ES6的普及，Lodash的功能或多或少会被原生功能所替代，所以使用时还需要进一步甄别，建议优先使用原生函数，有关ES6替代Lodash的部分，请参考文章<a href="https://link.jianshu.com/?t=http://www.zcfy.cc/article/10-lodash-features-you-can-replace-with-es6-467.html" target="_blank" rel="external nofollow noopener noreferrer">《10 个可用 ES6 替代的 Lodash 特性》</a>。</p></blockquote><p>其中有两处分别值得一看：<br></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用箭头函数创建可复用的路径</span></span><br><span class="line"><span class="keyword">const</span> object = &#123; <span class="string">'a'</span>: [&#123; <span class="string">'b'</span>: &#123; <span class="string">'c'</span>: <span class="number">3</span> &#125; &#125;, <span class="number">4</span>] &#125;;</span><br><span class="line"></span><br><span class="line">[</span><br><span class="line">    obj =&gt; obj.a[<span class="number">0</span>].b.c,</span><br><span class="line">    obj =&gt; ojb.a[<span class="number">1</span>]</span><br><span class="line">].map(<span class="function"><span class="params">path</span> =&gt;</span> path(object));</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用箭头函数编写链式调用</span></span><br><span class="line"><span class="keyword">const</span> pipe = <span class="function"><span class="params">function</span> =&gt;</span> <span class="function"><span class="params">data</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> functions.reduce(</span><br><span class="line">        (value, func) =&gt; func(value),</span><br><span class="line">        data</span><br><span class="line">    );</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> pipeline = pipe([</span><br><span class="line">    x =&gt; x * <span class="number">2</span>,</span><br><span class="line">    x =&gt; x / <span class="number">3</span>,</span><br><span class="line">    x =&gt; x &gt; <span class="number">5</span>,</span><br><span class="line">    b =&gt; !b</span><br><span class="line">]);</span><br><span class="line"></span><br><span class="line">pipeline(<span class="number">5</span>);</span><br><span class="line"><span class="comment">// true</span></span><br><span class="line">pipeline(<span class="number">20</span>);</span><br><span class="line"><span class="comment">// false</span></span><br></pre></td></tr></table></figure><br>在ES6中，如果一个函数只接收一个形参且函数提示一个 return 语句， 就可以使用箭头函数简化为：<br><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> func = <span class="function"><span class="params">p</span> =&gt;</span> v;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 类似于（不完全相同）</span></span><br><span class="line"><span class="keyword">const</span> func = <span class="function"><span class="keyword">function</span>(<span class="params">p</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> v;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>当有多重嵌套时，可以简化为：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> func = <span class="function"><span class="params">a</span> =&gt;</span> <span class="function"><span class="params">b</span> =&gt;</span> <span class="function"><span class="params">c</span> =&gt;</span> a + b + c;</span><br><span class="line">func(<span class="number">1</span>)(<span class="number">2</span>)(<span class="number">3</span>);</span><br><span class="line"><span class="comment">// =&gt; 6</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 类似于</span></span><br><span class="line"><span class="keyword">const</span> func = <span class="function"><span class="keyword">function</span> (<span class="params">a</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params">b</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params">c</span>) </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> a + b + c;</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>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;/images/lodash_0.jpg&quot; alt=&quot;timg&quot;&gt;&lt;/p&gt;&lt;p&gt;有几年开发经验的工程师，往往都会有自己的一套工具库，称为utils、helpers等等，这套库一方面是自己的技术积累，另一方面也是对某项技术的扩展，领先于技术规范的制订和实现。&lt;br&gt;Lodash就是这样的一套工具库，它内部封装了诸多对字符串、数组、对象等常见数据类型的处理函数，其中部分是目前ECMAScript尚未制订的规范，但同时被业界所认可的辅助函数。而且每天使用npm安装Lodash的数量在百万级以上，这在一定程度上证明了其代码的健壮性，值得我们在项目中一试。&lt;br&gt;
    
    </summary>
    
    
      <category term="JavaScript" scheme="http://blog.develophelper.com/categories/javascript/"/>
    
    
      <category term="JavaScript" scheme="http://blog.develophelper.com/tags/javascript/"/>
    
  </entry>
  
</feed>
