<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>LJQ&#39;s Blog</title>
  
  <subtitle>但行好事，莫问前程</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="https://github.com/TemplarJQ/"/>
  <updated>2019-07-13T05:19:30.881Z</updated>
  <id>https://github.com/TemplarJQ/</id>
  
  <author>
    <name>Jiaqi Liu</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>SpringBoot秒杀商城</title>
    <link href="https://github.com/TemplarJQ/2019/07/02/SpringBoot%E7%A7%92%E6%9D%80%E5%95%86%E5%9F%8E/"/>
    <id>https://github.com/TemplarJQ/2019/07/02/SpringBoot秒杀商城/</id>
    <published>2019-07-02T03:09:55.000Z</published>
    <updated>2019-07-13T05:19:30.881Z</updated>
    
    <content type="html"><![CDATA[<h1 id="SpringBoot秒杀商城"><a href="#SpringBoot秒杀商城" class="headerlink" title="SpringBoot秒杀商城"></a>SpringBoot秒杀商城</h1><p>SpringBoot并非什么新的框架，SpringBoot就是Spring + Boot，如同Maven整合了所有的jar包一样，SpringBoot整合了所有框架，并通过main函数启动。</p><h2 id="开发设计"><a href="#开发设计" class="headerlink" title="开发设计"></a>开发设计</h2><h3 id="1-1-项目建立"><a href="#1-1-项目建立" class="headerlink" title="1.1 项目建立"></a>1.1 项目建立</h3><ul><li>maven-archetype-quickstart建立SpringBoot项目</li></ul><br><br><br>- 引入parent依赖与starter依赖<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></pre></td><td class="code"><pre><span class="line">&lt;parent&gt;</span><br><span class="line">    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;</span><br><span class="line">    &lt;artifactId&gt;spring-boot-starter-parent&lt;/artifactId&gt;</span><br><span class="line">    &lt;version&gt;2.1.6.RELEASE&lt;/version&gt;</span><br><span class="line">  &lt;/parent&gt;</span><br><span class="line"></span><br><span class="line">&lt;dependency&gt;</span><br><span class="line">    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;</span><br><span class="line">    &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;</span><br><span class="line">  &lt;/dependency&gt;</span><br></pre></td></tr></table></figure><ul><li><p>加入注解使得SpringBoot开始运行</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">@EnableAutoConfiguration # Spring化</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">App</span> </span></span><br><span class="line"><span class="class"></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></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        System.out.println( <span class="string">"Hello World!"</span> );</span><br><span class="line">        SpringApplication.run(App.class, args); # 将App变成bean的形式</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>增加rest式外部访问流程</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">@EnableAutoConfiguration</span></span><br><span class="line">@RestController # rest访问控制</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">App</span> </span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    @RequestMapping("/") # rest映射</span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">home</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"></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></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        System.out.println( <span class="string">"Hello World!"</span> );</span><br><span class="line">        SpringApplication.run(App.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><h3 id="1-2-文件配置"><a href="#1-2-文件配置" class="headerlink" title="1.2 文件配置"></a>1.2 文件配置</h3><ul><li>加入application.propertites配置文件就可以配置端口等</li><li><p>配置数据库</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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">&lt;!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --&gt;</span><br><span class="line">    &lt;dependency&gt;</span><br><span class="line">      &lt;groupId&gt;mysql&lt;/groupId&gt;</span><br><span class="line">      &lt;artifactId&gt;mysql-connector-java&lt;/artifactId&gt;</span><br><span class="line">      &lt;version&gt;6.0.6&lt;/version&gt;</span><br><span class="line">    &lt;/dependency&gt;</span><br><span class="line">&lt;!-- https://mvnrepository.com/artifact/com.alibaba/druid --&gt;</span><br><span class="line">    &lt;dependency&gt;</span><br><span class="line">      &lt;groupId&gt;com.alibaba&lt;/groupId&gt;</span><br><span class="line">      &lt;artifactId&gt;druid&lt;/artifactId&gt;</span><br><span class="line">      &lt;version&gt;1.1.10&lt;/version&gt;</span><br><span class="line">    &lt;/dependency&gt;</span><br><span class="line">&lt;!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter --&gt;</span><br><span class="line">  &lt;dependency&gt;</span><br><span class="line">      &lt;groupId&gt;org.mybatis.spring.boot&lt;/groupId&gt;</span><br><span class="line">      &lt;artifactId&gt;mybatis-spring-boot-starter&lt;/artifactId&gt;</span><br><span class="line">      &lt;version&gt;1.3.2&lt;/version&gt;</span><br><span class="line">  &lt;/dependency&gt;</span><br></pre></td></tr></table></figure></li><li><p>在application.properties中加入mybatis.mapper-locations=classpath:mapping/*.xml支持</p></li><li><p>引入mybatis的自动生成插件</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></pre></td><td class="code"><pre><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.mybatis.generator<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-generator-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">version</span>&gt;</span>1.3.5<span class="tag">&lt;/<span class="name">version</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 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.generator<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-generator-core<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">       <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.3.5<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">       <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">       <span 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">version</span>&gt;</span>5.1.37<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">       <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">       <span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line">       <span class="tag">&lt;<span class="name">executions</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;<span class="name">execution</span>&gt;</span></span><br><span class="line">           <span class="tag">&lt;<span class="name">id</span>&gt;</span>mybatis generator<span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line">           <span class="tag">&lt;<span class="name">phase</span>&gt;</span>package<span class="tag">&lt;/<span class="name">phase</span>&gt;</span></span><br><span class="line">           <span class="tag">&lt;<span class="name">goals</span>&gt;</span></span><br><span class="line">             <span class="tag">&lt;<span class="name">goal</span>&gt;</span>generate<span class="tag">&lt;/<span class="name">goal</span>&gt;</span></span><br><span class="line">           <span class="tag">&lt;/<span class="name">goals</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;/<span class="name">execution</span>&gt;</span></span><br><span class="line">       <span class="tag">&lt;/<span class="name">executions</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="comment">&lt;!--允许移动生成文件--&gt;</span></span><br><span class="line">         <span class="tag">&lt;<span class="name">verbose</span>&gt;</span>true<span class="tag">&lt;/<span class="name">verbose</span>&gt;</span></span><br><span class="line">         <span class="comment">&lt;!--允许自动覆盖--&gt;</span></span><br><span class="line">         <span class="comment">&lt;!--这个一般企业开发不能覆盖，否则别人的成果会受影响--&gt;</span></span><br><span class="line">         <span class="tag">&lt;<span class="name">overwrite</span>&gt;</span>true<span class="tag">&lt;/<span class="name">overwrite</span>&gt;</span></span><br><span class="line">         <span class="comment">&lt;!--这个极其重要--&gt;</span></span><br><span class="line">         <span class="tag">&lt;<span class="name">configurationFile</span>&gt;</span></span><br><span class="line">             src/main/resources/mybatis-generator.xml</span><br><span class="line">           <span class="tag">&lt;/<span class="name">configurationFile</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">configuration</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;/<span class="name">plugin</span>&gt;</span></span><br></pre></td></tr></table></figure></li><li><p>mybatis-generator的官方文档的地址<br><a href="http://www.mybatis.org/generator/configreference/xmlconfig.html" target="_blank" rel="noopener">官方文档</a></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></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 generatorConfiguration</span></span><br><span class="line"><span class="meta">        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"</span></span><br><span class="line"><span class="meta">        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">generatorConfiguration</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--JDBC驱动jar包的位置--&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--&lt;classPathEntry location="C:/workspace/project/learning/mybatis/lib/mysql-connector-java-5.1.6.jar"/&gt;--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">context</span> <span class="attr">id</span>=<span class="string">"default"</span> <span class="attr">targetRuntime</span>=<span class="string">"MyBatis3"</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!--创建Java类时是否取消生成注释--&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">commentGenerator</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">"suppressDate"</span> <span class="attr">value</span>=<span class="string">"true"</span>/&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">"suppressAllComments"</span> <span class="attr">value</span>=<span class="string">"true"</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">commentGenerator</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!--JDBC数据库连接--&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">jdbcConnection</span> <span class="attr">driverClass</span>=<span class="string">"com.mysql.jdbc.Driver"</span></span></span><br><span class="line"><span class="tag">                        <span class="attr">connectionURL</span>=<span class="string">"jdbc:mysql://localhost:3306/test"</span></span></span><br><span class="line"><span class="tag">                        <span class="attr">userId</span>=<span class="string">"root"</span></span></span><br><span class="line"><span class="tag">                        <span class="attr">password</span>=<span class="string">"dev"</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">jdbcConnection</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!--</span></span><br><span class="line"><span class="comment">        Model模型生成器,用来生成含有主键key的类，记录类 以及查询Example类</span></span><br><span class="line"><span class="comment">        targetPackage     指定生成的model生成所在的包名</span></span><br><span class="line"><span class="comment">        targetProject     指定在该项目下所在的路径</span></span><br><span class="line"><span class="comment">        --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">javaModelGenerator</span> <span class="attr">targetPackage</span>=<span class="string">"dulk.learn.mybatis.generator.pojo"</span></span></span><br><span class="line"><span class="tag">                            <span class="attr">targetProject</span>=<span class="string">"src/main/java"</span>&gt;</span></span><br><span class="line">         <span class="comment">&lt;!-- 是否允许子包，即targetPackage.schemaName.tableName --&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">"enableSubPackages"</span> <span class="attr">value</span>=<span class="string">"true"</span>/&gt;</span></span><br><span class="line">            <span class="comment">&lt;!-- 是否对model添加构造函数 --&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">"constructorBased"</span> <span class="attr">value</span>=<span class="string">"true"</span>/&gt;</span></span><br><span class="line">            <span class="comment">&lt;!-- 是否对类CHAR类型的列的数据进行trim操作 --&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">"trimStrings"</span> <span class="attr">value</span>=<span class="string">"true"</span>/&gt;</span></span><br><span class="line">            <span class="comment">&lt;!-- 建立的Model对象是否 不可改变  即生成的Model对象不会有 setter方法，只有构造方法 --&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">"immutable"</span> <span class="attr">value</span>=<span class="string">"false"</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">javaModelGenerator</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!--</span></span><br><span class="line"><span class="comment">        mapper映射文件生成所在的目录 为每一个数据库的表生成对应的SqlMap文件 </span></span><br><span class="line"><span class="comment">        --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">sqlMapGenerator</span> <span class="attr">targetPackage</span>=<span class="string">"generator"</span></span></span><br><span class="line"><span class="tag">                         <span class="attr">targetProject</span>=<span class="string">"src/main/resources"</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">"enableSubPackages"</span> <span class="attr">value</span>=<span class="string">"true"</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">sqlMapGenerator</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- </span></span><br><span class="line"><span class="comment">        客户端代码，生成易于使用的针对Model对象和XML配置文件的代码</span></span><br><span class="line"><span class="comment">        type="ANNOTATEDMAPPER",生成Java Model和基于注解的Mapper对象</span></span><br><span class="line"><span class="comment">        type="MIXEDMAPPER",生成基于注解的Java Model和相应的Mapper对象</span></span><br><span class="line"><span class="comment">        type="XMLMAPPER",生成SQLMap XML文件和独立的Mapper接口</span></span><br><span class="line"><span class="comment">        --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">javaClientGenerator</span> <span class="attr">type</span>=<span class="string">"XMLMAPPER"</span></span></span><br><span class="line"><span class="tag">                             <span class="attr">targetPackage</span>=<span class="string">"com.xxx.dao"</span></span></span><br><span class="line"><span class="tag">                             <span class="attr">targetProject</span>=<span class="string">"src/main/java"</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">"enableSubPackages"</span> <span class="attr">value</span>=<span class="string">"true"</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">javaClientGenerator</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!--tables表及类名--&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">table</span> <span class="attr">tableName</span>=<span class="string">"author"</span> <span class="attr">domainObjectName</span>=<span class="string">"Author"</span></span></span><br><span class="line"><span class="tag">               <span class="attr">enableCountByExample</span>=<span class="string">"false"</span> <span class="attr">enableUpdateByExample</span>=<span class="string">"false"</span></span></span><br><span class="line"><span class="tag">               <span class="attr">enableDeleteByExample</span>=<span class="string">"false"</span> <span class="attr">enableSelectByExample</span>=<span class="string">"false"</span></span></span><br><span class="line"><span class="tag">               <span class="attr">selectByExampleQueryId</span>=<span class="string">"false"</span>&gt;</span></span><br><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">table</span> <span class="attr">tableName</span>=<span class="string">"book"</span> <span class="attr">domainObjectName</span>=<span class="string">"Book"</span></span></span><br><span class="line"><span class="tag">               <span class="attr">enableCountByExample</span>=<span class="string">"false"</span> <span class="attr">enableUpdateByExample</span>=<span class="string">"false"</span></span></span><br><span class="line"><span class="tag">               <span class="attr">enableDeleteByExample</span>=<span class="string">"false"</span> <span class="attr">enableSelectByExample</span>=<span class="string">"false"</span></span></span><br><span class="line"><span class="tag">               <span class="attr">selectByExampleQueryId</span>=<span class="string">"false"</span>&gt;</span></span><br><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">context</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">generatorConfiguration</span>&gt;</span></span><br></pre></td></tr></table></figure></li></ul><p>这是一种比较详细的配置方式。</p><h3 id="2-1-表结构设计及自动生成"><a href="#2-1-表结构设计及自动生成" class="headerlink" title="2.1 表结构设计及自动生成"></a>2.1 表结构设计及自动生成</h3><ul><li>1）密码和表结构是分开设计的，为user_info与user_password，且密码要加密存入</li><li>2）设置好plugins和具体的配置文件之后，即可通过generator生成一个新的mybatis文件，注意tables等指标都要重新设计</li><li>3）之后在Run的配置栏中选中edit configuration配置，新建maven，然后配置mybatis-generator命令并执行</li></ul><hr><p><em>出现如下报错，是数据库版本问题。</em><a href="https://blog.csdn.net/qq_21870555/article/details/80711187" target="_blank" rel="noopener">CSDN</a><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">Unknown system variable &apos;query_cache_size&apos;</span><br></pre></td></tr></table></figure></p><hr><ul><li><p>注意把table中的几个字段设置为自动生成false后可以删除掉生成的Example</p></li><li><p>在application下配置</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></pre></td><td class="code"><pre><span class="line">spring.datasource.name=seckillmall</span><br><span class="line">spring.datasource.url=jdbc:mysql://xxxx:3306/seckillmall</span><br><span class="line">spring.datasource.data-username=&quot;xxx&quot;</span><br><span class="line">spring.datasource.data-password=&quot;xxx&quot;</span><br><span class="line"></span><br><span class="line"># 使用druid数据源</span><br><span class="line">spring.datasource.type=com.alibaba.druid.pool.DruidDataSource</span><br><span class="line">spring.datasource.driver-class-name=com.mysql.jdbc.Driver</span><br></pre></td></tr></table></figure></li></ul><h3 id="2-2-修改App主入口"><a href="#2-2-修改App主入口" class="headerlink" title="2.2 修改App主入口"></a>2.2 修改App主入口</h3><ul><li><p>加入spring新注解扫描，替换之前的</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">@SpringBootApplication</span>(scanBasePackages = &#123;<span class="string">"com.seckillmall"</span>&#125;)</span><br><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="meta">@MapperScan</span>(<span class="string">"com.seckillmall.dao"</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">App</span> </span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> UserDOMapper userDOMapper;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@RequestMapping</span>(<span class="string">"/"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">home</span><span class="params">()</span></span>&#123;</span><br><span class="line">        UserDO userDO = userDOMapper.selectByPrimaryKey(<span class="number">1</span>);</span><br><span class="line">        <span class="keyword">if</span>(userDO == <span class="keyword">null</span>)&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="string">"用户不存在"</span>;</span><br><span class="line">        &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">            <span class="keyword">return</span> userDO.getName();</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></li><li><p><em>注：这里有一个BUG，Mybatis的自动生成覆写很智障!!每次都会在头部插入新的代码段，需要手动控制，不然会报resultMap错误。</em></p></li><li>*注2：这里还有一个问题，Druid的配置过程中参数一定要写对，有的时候是spring.datasource.username而不是spring.datasource.data-username</li><li><em>注3：报错Communications link failure的时候直接刷新一下重启就好了，可能是TCP连接的重建问题。</em></li></ul><h3 id="3-1-开发用户信息业务逻辑"><a href="#3-1-开发用户信息业务逻辑" class="headerlink" title="3.1 开发用户信息业务逻辑"></a>3.1 开发用户信息业务逻辑</h3><ul><li>1）先构建出UserController和UserService这些MVC常见组件，用于实现查询业务逻辑；</li><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><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Controller</span>(<span class="string">"user"</span>)</span><br><span class="line"><span class="meta">@RequestMapping</span>(<span class="string">"/user"</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><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> UserServiceImpl userService;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@RequestMapping</span>(<span class="string">"/get"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">getUser</span><span class="params">(@RequestParam(name=<span class="string">"id"</span>)</span>Integer id)</span>&#123;</span><br><span class="line">        <span class="comment">//调用service服务并获取对象</span></span><br><span class="line">        UserModel userModel = userService.getUserById(id);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>2）这里引入一个重要概念是<em>UserModel</em>用于实现对前端的交互，组装了info和passward信息，因为Dao层的东西是不能透传给前端的；注意还要构建Model的从Dao转化到Model的方法</p></li><li><p>UserModel</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UserServiceImpl</span> <span class="keyword">implements</span> <span class="title">UserService</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> UserDOMapper userDOMapper;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//缺乏password的查询返回方法</span></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> UserModel <span class="title">getUserById</span><span class="params">(Integer id)</span> </span>&#123;</span><br><span class="line">        UserDO userDO = userDOMapper.selectByPrimaryKey(id);</span><br><span class="line">        <span class="comment">//缺乏返回</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> UserModel <span class="title">convertFromDataObject</span><span class="params">(UserDO userDO, UserPassword userPassword)</span></span>&#123;</span><br><span class="line">        UserModel userModel = <span class="keyword">new</span> UserModel();</span><br><span class="line">        <span class="keyword">if</span>(userDO == <span class="keyword">null</span>)&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        BeanUtils.copyProperties(userDO,userModel);</span><br><span class="line">        <span class="keyword">if</span>(userPassword != <span class="keyword">null</span>)&#123;</span><br><span class="line">            userModel.setEncrptPassword(userPassword.getEncrptPassword());</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>3）引入自动化的然后改造Mybatis文件，进行下一步的处理后在service中进行修改</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UserServiceImpl</span> <span class="keyword">implements</span> <span class="title">UserService</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> UserDOMapper userDOMapper;</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> UserPasswordMapper userPasswordMapper;</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> UserModel <span class="title">getUserById</span><span class="params">(Integer id)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//查询对应的业务逻辑</span></span><br><span class="line">        UserDO userDO = userDOMapper.selectByPrimaryKey(id);</span><br><span class="line">        <span class="comment">//用户不存在</span></span><br><span class="line">        <span class="keyword">if</span>(userDO == <span class="keyword">null</span>)&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//查询密码等加密信息</span></span><br><span class="line">        UserPassword userPassword = userPasswordMapper.selectByUserId(id);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> convertFromDataObject(userDO, userPassword);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> UserModel <span class="title">convertFromDataObject</span><span class="params">(UserDO userDO, UserPassword userPassword)</span></span>&#123;</span><br><span class="line">        UserModel userModel = <span class="keyword">new</span> UserModel();</span><br><span class="line">        <span class="keyword">if</span>(userDO == <span class="keyword">null</span>)&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        BeanUtils.copyProperties(userDO,userModel);</span><br><span class="line">        <span class="keyword">if</span>(userPassword != <span class="keyword">null</span>)&#123;</span><br><span class="line">            userModel.setEncrptPassword(userPassword.getEncrptPassword());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> userModel;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>改进之后的service</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UserServiceImpl</span> <span class="keyword">implements</span> <span class="title">UserService</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> UserDOMapper userDOMapper;</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> UserPasswordMapper userPasswordMapper;</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> UserModel <span class="title">getUserById</span><span class="params">(Integer id)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//查询对应的业务逻辑</span></span><br><span class="line">        UserDO userDO = userDOMapper.selectByPrimaryKey(id);</span><br><span class="line">        <span class="comment">//用户不存在</span></span><br><span class="line">        <span class="keyword">if</span>(userDO == <span class="keyword">null</span>)&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//查询密码等加密信息</span></span><br><span class="line">        UserPassword userPassword = userPasswordMapper.selectByUserId(id);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> convertFromDataObject(userDO, userPassword);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> UserModel <span class="title">convertFromDataObject</span><span class="params">(UserDO userDO, UserPassword userPassword)</span></span>&#123;</span><br><span class="line">        UserModel userModel = <span class="keyword">new</span> UserModel();</span><br><span class="line">        <span class="keyword">if</span>(userDO == <span class="keyword">null</span>)&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        BeanUtils.copyProperties(userDO,userModel);</span><br><span class="line">        <span class="keyword">if</span>(userPassword != <span class="keyword">null</span>)&#123;</span><br><span class="line">            userModel.setEncrptPassword(userPassword.getEncrptPassword());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> userModel;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><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><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="meta">@Controller</span>(<span class="string">"user"</span>)</span><br><span class="line"><span class="meta">@RequestMapping</span>(<span class="string">"/user"</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><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> UserServiceImpl userService;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@RequestMapping</span>(<span class="string">"/get"</span>)</span><br><span class="line">    <span class="meta">@ResponseBody</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> UserModel <span class="title">getUser</span><span class="params">(@RequestParam(name=<span class="string">"id"</span>)</span>Integer id)</span>&#123;</span><br><span class="line">        <span class="comment">//调用service服务并获取对象</span></span><br><span class="line">        UserModel userModel = userService.getUserById(id);</span><br><span class="line">        <span class="keyword">return</span> userModel;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p><em>注：没有写@ResponsBody后就会发现请求会无法显示成为json格式</em></p></li></ul><h3 id="3-2-重构改进"><a href="#3-2-重构改进" class="headerlink" title="3.2 重构改进"></a>3.2 重构改进</h3><ul><li>这样直接返回Json格式下用户密码，即便是加密格式下的，也会不是很好的设计方式，因此对它进行改造。</li><li><p>加入UserVO即ViewObject即可观察到最终的情况。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UserVO</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Integer id;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Byte gender;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Integer age;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String telphone;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>现在的模式不允许任何错误，需要对它进行下一步的调整，需要归一化，建立response.CommonReturnType模式，将业务逻辑错误与服务器错误区分开来</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="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CommonReturnType</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//处理结果有"success"和"fail"</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String status;</span><br><span class="line">    <span class="keyword">private</span> Object data;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><h3 id="3-3-错误异常处理"><a href="#3-3-错误异常处理" class="headerlink" title="3.3 错误异常处理"></a>3.3 错误异常处理</h3><ul><li><p>定义枚举类对错误信息进行处理</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">enum</span> EmBusinessError implements CommonError &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//通用错误类型00001，解决入参校验</span></span><br><span class="line">    PARAMETER_VALIDATION_ERROR(<span class="number">00001</span>, <span class="string">"参数不合法"</span>),</span><br><span class="line"></span><br><span class="line">    <span class="comment">//10000开头表示为用户信息相关定义错误</span></span><br><span class="line">    User_NOT_EXIST(<span class="number">10001</span>, <span class="string">"用户不存在"</span>)</span><br><span class="line">    ;</span><br><span class="line"></span><br><span class="line">    EmBusinessError(<span class="keyword">int</span> errCode, String errMsg) &#123;</span><br><span class="line">        <span class="keyword">this</span>.errCode = errCode;</span><br><span class="line">        <span class="keyword">this</span>.errMsg = errMsg;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> errCode;</span><br><span class="line">    <span class="keyword">private</span> String errMsg;</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">int</span> <span class="title">getErrCode</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.errCode;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getErrMsg</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.errMsg;</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> CommonError <span class="title">setErrMsg</span><span class="params">(String errMsg)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.errMsg = errMsg;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>处理流程为将所有异常抛到controller上的handler上进行处理，使用包装器模式构建Exception</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//包装器业务异常类实现</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BusinessException</span> <span class="keyword">extends</span> <span class="title">Exception</span> <span class="keyword">implements</span> <span class="title">CommonError</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//强关联一个Error</span></span><br><span class="line">    <span class="keyword">private</span> CommonError commonError;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//直接接受EmBusinessError的传参用于构造业务异常</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">BusinessException</span><span class="params">(CommonError commonError)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>();</span><br><span class="line">        <span class="keyword">this</span>.commonError = commonError;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//接受自定义ErrorMsg的方式接受业务异常</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">BusinessException</span><span class="params">(CommonError commonError, String errMsg)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>();</span><br><span class="line">        <span class="keyword">this</span>.commonError = commonError;</span><br><span class="line">        <span class="keyword">this</span>.setErrMsg(errMsg);</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">int</span> <span class="title">getErrCode</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.commonError.getErrCode();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getErrMsg</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.commonError.getErrMsg();</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> CommonError <span class="title">setErrMsg</span><span class="params">(String errMsg)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.commonError.setErrMsg(errMsg);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><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><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">@RequestMapping</span>(<span class="string">"/get"</span>)</span><br><span class="line"><span class="meta">@ResponseBody</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> CommonReturnType <span class="title">getUser</span><span class="params">(@RequestParam(name=<span class="string">"id"</span>)</span>Integer id) <span class="keyword">throws</span> BusinessException </span>&#123;</span><br><span class="line">    <span class="comment">//调用service服务并获取对象</span></span><br><span class="line">    UserModel userModel = userService.getUserById(id);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(userModel == <span class="keyword">null</span>)&#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> BusinessException(EmBusinessError.User_NOT_EXIST);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//将核心领域模型用户转UI给用户使用的模型</span></span><br><span class="line">    UserVO userVO = convertFromModel(userModel);</span><br><span class="line">    <span class="keyword">return</span> CommonReturnType.create(userVO);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>怎么拦截这个tomcat的异常处理过程显示呢？定义ExceptionHandler来解决未被controller层吸收的exception，采用@ResponseBody模式产生json。优化后的代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//定义ExceptionHandler解决未被controller层吸收的exception</span></span><br><span class="line">    <span class="meta">@ExceptionHandler</span>(Exception.class)</span><br><span class="line">    <span class="meta">@ResponseStatus</span>(HttpStatus.OK)<span class="comment">//屏蔽tomcat自己的处理</span></span><br><span class="line">    <span class="meta">@ResponseBody</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">handlerException</span><span class="params">(HttpServletRequest request, Exception ex)</span> </span>&#123;</span><br><span class="line">        </span><br><span class="line">        BusinessException exception = (BusinessException) ex;</span><br><span class="line">        Map&lt;String, Object&gt; responseData = <span class="keyword">new</span> HashMap&lt;&gt;();</span><br><span class="line">        responseData.put(<span class="string">"errCode"</span>, exception.getErrCode());</span><br><span class="line">        responseData.put(<span class="string">"errMsg"</span>, exception.getErrMsg());</span><br><span class="line">        <span class="keyword">return</span> CommonReturnType.create(responseData, <span class="string">"fail"</span>);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></li><li><p>添加BaseController后我们仍旧可以让UserController去继承该类。</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="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BaseController</span> </span>&#123;</span><br><span class="line">    <span class="comment">//定义ExceptionHandler解决未被controller层吸收的exception</span></span><br><span class="line">    <span class="meta">@ExceptionHandler</span>(Exception.class)</span><br><span class="line">    <span class="meta">@ResponseStatus</span>(HttpStatus.OK)<span class="comment">//屏蔽tomcat自己的处理</span></span><br><span class="line">    <span class="meta">@ResponseBody</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">handlerException</span><span class="params">(HttpServletRequest request, Exception ex)</span> </span>&#123;</span><br><span class="line">        Map&lt;String, Object&gt; responseData = <span class="keyword">new</span> HashMap&lt;&gt;();</span><br><span class="line">        <span class="keyword">if</span>(ex <span class="keyword">instanceof</span> BusinessException) &#123;</span><br><span class="line">            BusinessException exception = (BusinessException) ex;</span><br><span class="line">            responseData.put(<span class="string">"errCode"</span>, exception.getErrCode());</span><br><span class="line">            responseData.put(<span class="string">"errMsg"</span>, exception.getErrMsg());</span><br><span class="line">        &#125;<span class="keyword">else</span> &#123;</span><br><span class="line">            responseData.put(<span class="string">"errCode"</span>, EmBusinessError.UNKNOWN_ERROR.getErrCode());</span><br><span class="line">            responseData.put(<span class="string">"errMSg"</span>, EmBusinessError.UNKNOWN_ERROR.getErrMsg());</span><br><span class="line"></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> CommonReturnType.create(responseData, <span class="string">"fail"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><h2 id="otp用户注册模块"><a href="#otp用户注册模块" class="headerlink" title="otp用户注册模块"></a>otp用户注册模块</h2><h3 id="1-1-用户获取otp短信"><a href="#1-1-用户获取otp短信" class="headerlink" title="1.1 用户获取otp短信"></a>1.1 用户获取otp短信</h3><ul><li><p>首先的一个设计是生成otp代码，这个一般是通过购买第三方的服务实现的，而我们这里采用随机数生成的方式进行。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//获取otp随机码</span></span><br><span class="line">        Random random = <span class="keyword">new</span> Random();</span><br><span class="line">        <span class="keyword">int</span> randomCode = random.nextInt(<span class="number">99999</span>);</span><br><span class="line">        String optCode = String.valueOf(randomCode + <span class="number">10000</span>);</span><br></pre></td></tr></table></figure></li><li><p>然后的一个设计为将otp与用户代码进行绑定，这一步一般是使用redis进行操作，这里采用简单的绑定到httpsession中进行处理。</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="comment">//与Httpsession进行绑定</span></span><br><span class="line">        httpServletRequest.setAttribute(telphone, optCode);</span><br></pre></td></tr></table></figure></li><li><p>最后是输出otpcode，这里使用控制台输出检验</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="meta">@RequestMapping</span>(<span class="string">"/getotp"</span>)</span><br><span class="line">    <span class="meta">@ResponseBody</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> CommonReturnType <span class="title">getOtp</span><span class="params">(@RequestParam(name=<span class="string">"telphone"</span>)</span>String telphone)</span>&#123;</span><br><span class="line">        <span class="comment">//获取otp随机码</span></span><br><span class="line">        Random random = <span class="keyword">new</span> Random();</span><br><span class="line">        <span class="keyword">int</span> randomCode = random.nextInt(<span class="number">99999</span>);</span><br><span class="line">        String optCode = String.valueOf(randomCode + <span class="number">10000</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//与Httpsession进行绑定</span></span><br><span class="line">        httpServletRequest.getSession().setAttribute(telphone, optCode);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//将optcode返回给用户</span></span><br><span class="line">        System.out.println(<span class="string">"telphone = "</span>+ telphone + <span class="string">" &amp; optdoe = "</span>+ optCode);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> CommonReturnType.create(<span class="keyword">null</span>);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></li></ul><h3 id="1-2-用户注册逻辑实现"><a href="#1-2-用户注册逻辑实现" class="headerlink" title="1.2 用户注册逻辑实现"></a>1.2 用户注册逻辑实现</h3><ul><li><p>先校验optcode</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> CommonReturnType <span class="title">register</span><span class="params">(@RequestParam(name=<span class="string">"telphone"</span>)</span>String telphone,</span></span><br><span class="line"><span class="function">                                 @<span class="title">RequestParam</span><span class="params">(name = <span class="string">"id"</span>)</span>Integer id,</span></span><br><span class="line"><span class="function">                                 @<span class="title">RequestParam</span><span class="params">(name = <span class="string">"name"</span>)</span>String name,</span></span><br><span class="line"><span class="function">                                 @<span class="title">RequestParam</span><span class="params">(name = <span class="string">"gender"</span>)</span>Byte gender,</span></span><br><span class="line"><span class="function">                                 @<span class="title">RequestParam</span><span class="params">(name = <span class="string">"age"</span>)</span>Integer age,</span></span><br><span class="line"><span class="function">                                 @<span class="title">RequestParam</span><span class="params">(name = <span class="string">"optcode"</span>)</span>String optCode</span></span><br><span class="line"><span class="function">                                 ) <span class="keyword">throws</span> BusinessException </span>&#123;</span><br><span class="line">    <span class="comment">//首先校验optcode</span></span><br><span class="line">    String inSessionCode = (String)<span class="keyword">this</span>.httpServletRequest.getSession().getAttribute(<span class="string">"telphone"</span>);</span><br><span class="line">    <span class="keyword">if</span>(!com.alibaba.druid.util.StringUtils.equals(inSessionCode, optCode))&#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, <span class="string">"注册短信验证错误"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">                                 &#125;</span><br></pre></td></tr></table></figure></li><li><p>增加两个对应的转化字段</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">register</span><span class="params">(UserModel userModel)</span> <span class="keyword">throws</span> BusinessException </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(userModel == <span class="keyword">null</span>)&#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, <span class="string">"用户信息为空"</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span>(org.apache.commons.lang3.StringUtils.isEmpty(userModel.getName())</span><br><span class="line">            || userModel.getGender() == <span class="keyword">null</span></span><br><span class="line">            || userModel.getAge() == <span class="keyword">null</span></span><br><span class="line">            || org.apache.commons.lang3.StringUtils.isEmpty(userModel.getTelphone()))&#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//实现model转化为dataobject</span></span><br><span class="line">        UserDO userDO = convertFromUserModel(userModel);</span><br><span class="line">        userDOMapper.insertSelective(userDO);</span><br><span class="line"></span><br><span class="line">        UserPassword userPassword = convertPasswordFromUserModel(userModel);</span><br><span class="line">        userPasswordMapper.insertSelective(userPassword);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> UserPassword <span class="title">convertPasswordFromUserModel</span><span class="params">(UserModel userModel)</span></span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(userModel == <span class="keyword">null</span>)&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        UserPassword userPassword = <span class="keyword">new</span> UserPassword();</span><br><span class="line">        userPassword.setEncrptPassword(userModel.getEncrptPassword());</span><br><span class="line">        userPassword.setId(userModel.getId());</span><br><span class="line">        <span class="keyword">return</span> userPassword;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> UserDO <span class="title">convertFromUserModel</span><span class="params">(UserModel userModel)</span></span>&#123;</span><br><span class="line">        UserDO userDO = <span class="keyword">new</span> UserDO();</span><br><span class="line">        <span class="keyword">if</span>(userModel == <span class="keyword">null</span>)&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        BeanUtils.copyProperties(userModel, userDO);</span><br><span class="line">        <span class="keyword">return</span> userDO;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></li><li><p>前后端连接，注意先在其中加上@RequestMethod字段和一个content_type类型</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="keyword">static</span> <span class="keyword">final</span> String CONTENT_TYPE_FORMED = <span class="string">"application/x-www-form-urlencoded"</span>;</span><br><span class="line">···</span><br><span class="line"><span class="meta">@RequestMapping</span>(value = <span class="string">"/getotp"</span>, method = &#123;RequestMethod.POST&#125;, consumes = &#123;CONTENT_TYPE_FORMED&#125;)</span><br></pre></td></tr></table></figure></li><li><p>然后处理ajax的跨域请求的问题为在controller上加入@CrossOrigin即可</p></li><li>对应的 xhrFields:{withCredentials:true}前端设置也是为了配合使用而设置的</li><li>注意BASE64的用法在JDK9之后发生了变化<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="function"><span class="keyword">public</span> String <span class="title">enCodeByMD5</span><span class="params">(String str)</span> <span class="keyword">throws</span> NoSuchAlgorithmException, UnsupportedEncodingException </span>&#123;</span><br><span class="line">        <span class="comment">// 确定计算方法</span></span><br><span class="line">        MessageDigest md5 = MessageDigest.getInstance(<span class="string">"MD5"</span>);</span><br><span class="line">        BASE64Encoder base64Encoder = <span class="keyword">new</span> BASE64Encoder();</span><br><span class="line">        <span class="comment">// 加密字符串</span></span><br><span class="line">        String newStr = base64Encoder.encode(md5.digest(str.getBytes(<span class="string">"utf-8"</span>)));</span><br><span class="line">        <span class="keyword">return</span> newStr;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></li></ul><p>解决地址：<a href="https://blog.csdn.net/Cha0DD/article/details/87794268" target="_blank" rel="noopener">jdk9之后</a></p><ul><li>解决数据库唯一索引问题，添加对手机号的索引之后，就可以添加对于异常的处理过程，在register里catch到</li></ul><h3 id="1-3-验证模块的改进"><a href="#1-3-验证模块的改进" class="headerlink" title="1.3 验证模块的改进"></a>1.3 验证模块的改进</h3><ul><li><p>增加依赖</p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&lt;dependency&gt;</span><br><span class="line">      &lt;groupId&gt;org.hibernate&lt;/groupId&gt;</span><br><span class="line">      &lt;artifactId&gt;hibernate-validator&lt;/artifactId&gt;</span><br><span class="line">      &lt;version&gt;5.2.4.Final&lt;/version&gt;</span><br><span class="line">    &lt;/dependency&gt;</span><br></pre></td></tr></table></figure></li><li><p>增加验证过程，使用hibernate自带的用户模块，新建ValidatorImpl，注意加注释能够使它在初始化过程中被扫描到</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ValidatorImpl</span> <span class="keyword">implements</span> <span class="title">InitializingBean</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Validator validator;</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> ValidationResult <span class="title">validate</span><span class="params">(Object bean)</span></span>&#123;</span><br><span class="line">        ValidationResult validationResult = <span class="keyword">new</span> ValidationResult();</span><br><span class="line">        Set&lt;ConstraintViolation&lt;Object&gt;&gt; constraintViolations = validator.validate(bean);</span><br><span class="line">        <span class="keyword">if</span>(constraintViolations.size() &gt; <span class="number">0</span>)&#123;</span><br><span class="line">            <span class="comment">//有错误</span></span><br><span class="line">            validationResult.setHasError(<span class="keyword">true</span>);</span><br><span class="line">            constraintViolations.forEach(constraintViolation -&gt; &#123;</span><br><span class="line">                String errMsg = constraintViolation.getMessage();</span><br><span class="line">                String propertyName = constraintViolation.getPropertyPath().toString();</span><br><span class="line">                validationResult.getErrMsgMap().put(propertyName, errMsg);</span><br><span class="line">            &#125;);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> validationResult;</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">afterPropertiesSet</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        <span class="comment">//将hibernate validator的工厂模式使其实例化</span></span><br><span class="line">        <span class="keyword">this</span>.validator = Validation.buildDefaultValidatorFactory().getValidator();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>然后配合注解的@NotBlank等就可以实现对于其的校验，这是一种hibernate+注解的方式</p></li></ul><h3 id="2-2-登录模块的修改"><a href="#2-2-登录模块的修改" class="headerlink" title="2.2 登录模块的修改"></a>2.2 登录模块的修改</h3><ul><li>业务逻辑注意有对于Mapper.xml的修改</li></ul><h2 id="商品模块的实现"><a href="#商品模块的实现" class="headerlink" title="商品模块的实现"></a>商品模块的实现</h2><hr><p>对于一个初级Java程序员来说，设计一个程序可能就是按照用户经理的UI设计去复现表结构，然后使用Mybatis对结构进行调整，但是这样设计是错误的。<br>一定要先设计<em>领域模型</em>，也就是Model</p><hr><h2 id="1-1-商品模块的设计与实现"><a href="#1-1-商品模块的设计与实现" class="headerlink" title="1.1 商品模块的设计与实现"></a>1.1 商品模块的设计与实现</h2><ul><li>首先修改pom中mybatis自动生成的覆写参数！！</li><li><p>然后在mybatis-generator上修改使其生成新的表参数</p></li><li><p>列出一个面向领域编程的典型设计service流程，service注解加上，然后Transactional主要保证对一个事务的读写</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ItemServiceImpl</span> <span class="keyword">implements</span> <span class="title">ItemService</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="meta">@Transactional</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> ItemModel <span class="title">createItem</span><span class="params">(ItemModel itemModel)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//校验入参</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">//转化ItemModel变为DataObject</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">//写入数据库</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">//返回创建对象</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>Service层尽量写得复杂，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><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></pre></td><td class="code"><pre><span class="line">Service</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ItemServiceImpl</span> <span class="keyword">implements</span> <span class="title">ItemService</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> ValidatorImpl validator;</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> ItemDOMapper itemDOMapper;</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> ItemStockDOMapper itemStockDOMapper;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> ItemDO <span class="title">convertFromItemModel</span><span class="params">(ItemModel itemModel)</span></span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(itemModel == <span class="keyword">null</span>)&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        ItemDO itemDO = <span class="keyword">new</span> ItemDO();</span><br><span class="line">        BeanUtils.copyProperties(itemModel, itemDO);</span><br><span class="line">        itemDO.setPrice(itemModel.getPrice().doubleValue());</span><br><span class="line">        <span class="keyword">return</span> itemDO;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> ItemStockDO <span class="title">convertStockFromItemModel</span><span class="params">(ItemModel itemModel)</span></span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(itemModel == <span class="keyword">null</span>)&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        ItemStockDO itemStockDO = <span class="keyword">new</span> ItemStockDO();</span><br><span class="line">        itemStockDO.setId(itemModel.getId());</span><br><span class="line">        itemStockDO.setStock(itemModel.getStock());</span><br><span class="line">        <span class="keyword">return</span> itemStockDO;</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="meta">@Transactional</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> ItemModel <span class="title">createItem</span><span class="params">(ItemModel itemModel)</span> <span class="keyword">throws</span> BusinessException </span>&#123;</span><br><span class="line">        <span class="comment">//校验入参</span></span><br><span class="line">        ValidationResult validationResult = validator.validate(itemModel);</span><br><span class="line">        <span class="keyword">if</span>(validationResult.isHasErrors())&#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, validationResult.getErrMsg());</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//转化ItemModel变为DataObject</span></span><br><span class="line">        ItemDO itemDO = <span class="keyword">this</span>.convertFromItemModel(itemModel);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//写入数据库</span></span><br><span class="line">        itemDOMapper.insertSelective(itemDO);</span><br><span class="line">        itemModel.setId(itemDO.getId());</span><br><span class="line"></span><br><span class="line">        ItemStockDO itemStockDO = <span class="keyword">this</span>.convertStockFromItemModel(itemModel);</span><br><span class="line">        itemStockDOMapper.insertSelective(itemStockDO);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//返回创建对象</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.getItemById(itemModel.getId());</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> List&lt;ItemModel&gt; <span class="title">listItem</span><span class="params">()</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> ItemModel <span class="title">getItemById</span><span class="params">(Integer id)</span> </span>&#123;</span><br><span class="line">        ItemDO itemDO = itemDOMapper.selectByPrimaryKey(id);</span><br><span class="line">        <span class="keyword">if</span>(itemDO == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//操作获得库存数量</span></span><br><span class="line">        ItemStockDO itemStockDO = itemStockDOMapper.selectByItemId( itemDO.getId());</span><br><span class="line"></span><br><span class="line">        <span class="comment">//将dataobject转换成model</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.convertModelFromDataObject(itemDO,itemStockDO);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> ItemModel <span class="title">convertModelFromDataObject</span><span class="params">(ItemDO itemDO,ItemStockDO itemStockDO)</span></span>&#123;</span><br><span class="line">        ItemModel itemModel = <span class="keyword">new</span> ItemModel();</span><br><span class="line">        BeanUtils.copyProperties(itemDO,itemModel);</span><br><span class="line">        itemModel.setPrice(<span class="keyword">new</span> BigDecimal(itemDO.getPrice()));</span><br><span class="line">        itemModel.setStock(itemStockDO.getStock());</span><br><span class="line">        <span class="keyword">return</span> itemModel;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></li><li><p>顺序是ItemModel -&gt; 生成表结构 -&gt; 写ItemService -&gt; 实现 -&gt; 写Controller层 -&gt; 写ItemVO用于返回给前端</p></li></ul><h3 id="1-3-前端展示商品"><a href="#1-3-前端展示商品" class="headerlink" title="1.3 前端展示商品"></a>1.3 前端展示商品</h3><ul><li>注意图片的引用格式</li></ul><h2 id="订单生成"><a href="#订单生成" class="headerlink" title="订单生成"></a>订单生成</h2><h3 id="1-1-订单号生成"><a href="#1-1-订单号生成" class="headerlink" title="1.1 订单号生成"></a>1.1 订单号生成</h3><ul><li><p>首先对订单进行Model设计</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//解决用户下单的交易模型</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">OrderModel</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//企业级别应用的交易号是要记录时间的明显格式，如20190701+88888</span></span><br><span class="line">    <span class="keyword">private</span> String id;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//购买的用户id</span></span><br><span class="line">    <span class="keyword">private</span> Integer userId;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//商品id</span></span><br><span class="line">    <span class="keyword">private</span> Integer itemId;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//购买商品的单价，这家伙是到时候写秒杀用的东西，变化的时候要用</span></span><br><span class="line">    <span class="keyword">private</span> BigDecimal itemPrice;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//数目</span></span><br><span class="line">    <span class="keyword">private</span> Integer amount;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//总金额</span></span><br><span class="line">    <span class="keyword">private</span> BigDecimal orderAmount;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>使用Mybatis-generator生成相应的Mapper和DO模型；</p></li><li>创建OrderService，构建create方法，传入的参数为用户和订单的id，以及相应的数量；</li><li>构建Service实现以及加上@service标注，重写方法，并保证事务是在同一个订单当中，@Transactional；</li><li>1）校验用户及商品存在-&gt;2）校验商品-&gt;3）落单并减库存/支付减库存（更难，且无法避免超卖问题，要造成退单）-&gt; 4）订单入库 -&gt; 5）返回前端</li></ul><hr><p>这里有两个可以展开来研究的点：</p><p>第一个就是一个落单减库存，虽然是合情合理的，但是可能被恶意下单，从而造成损失；<br>商家为了让用户及早进行交易的提交，采用支付减库存的方式，但是这样又会遇到超卖退单的问题，用户体验较差，因此需要进行进一步的处理，超卖问题的解决是依靠——备货来解决的，但是要有限度。</p><p>第二个就是在落单减库存的时候，我们要对stock进行操作，其实这里完全可以独立出一个Service操作对stock进行进一步的优化，可以展开来写。</p><hr><ul><li><p>详细步骤</p><ul><li>1）校验有三个信息并抛出异常</li><li>2）落单要产生新的update sql语句，并且返回int值，根据int值再来减库存，这些都是对于stock表的操作</li><li>3）注意操作都是在Service层之间互相调用的，隔离开来Mapper </li><li>4）产生新的订单信息，也要计算金额，并将Model转为DO</li><li>5）将DO写入数据库</li><li>6）但是这里id为主键（string）需要生成交易流水号：订单号16位，前八位为时间信息，年月日（用于归档消除数据库使用），中间6位为自增序列（保证订单号不重复，如果超过6位数字还要再增大），最后2位为分库分表位（00到99，对订单进行用户水平拆分）</li><li>为什么加了Transactional标签后反而private不能写入？</li></ul></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//分库分表路由信息</span></span><br><span class="line">Integer userId = <span class="number">1000122</span>;</span><br><span class="line">userId % <span class="number">100</span> <span class="comment">// 100个库的100个表里</span></span><br></pre></td></tr></table></figure><hr><p>这里还有一个问题就是对于sequence表解决序列数字问题的过程中，还有可能造成超出6位，这时就需要对表进行循环写入。<br>还有就是事务处理包含在了一个Transactional当中，失败回滚时候，序列号也需要被处理。这时修改get序列号的方法加入新的Transactional参数就可以完成修改。</p><hr><ul><li><p>构建Controller</p><ul><li>获取用户id</li><li>检查id后进行商品的订单处理，库存减少</li><li>调整Model和Dao的数量问题，使其对等（BeanUtils.copyPropertities）</li><li>对库存进行调整，销量增加</li></ul></li></ul><h2 id="秒杀模块的实现"><a href="#秒杀模块的实现" class="headerlink" title="秒杀模块的实现"></a>秒杀模块的实现</h2><h3 id="1-1-构建秒杀活动"><a href="#1-1-构建秒杀活动" class="headerlink" title="1.1 构建秒杀活动"></a>1.1 构建秒杀活动</h3><ul><li><p>构建出来的秒杀活动原型Model</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">PromoModel</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Integer id;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//秒杀活动名称</span></span><br><span class="line">    <span class="keyword">private</span> String promoName;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//秒杀活动的开始时间</span></span><br><span class="line">    <span class="keyword">private</span> DateTime startDate;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//秒杀活动的适用商品</span></span><br><span class="line">    <span class="keyword">private</span> Integer itemId;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//秒杀商品的活动折扣价</span></span><br><span class="line">    <span class="keyword">private</span> BigDecimal promoItemPrice;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Integer <span class="title">getId</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> id;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>注意Mysql数据库中的date数据类型为年月日时分秒类型，即：’0000-01-01 00:00:00’，这也是一个数据库的设置问题，有几种典型的处理问题，连接如下：<br><a href="https://blog.csdn.net/u011499484/article/details/80415417" target="_blank" rel="noopener">Mysql中datetime默认值问题</a></p></li></ul><h3 id="1-2-改进活动模型"><a href="#1-2-改进活动模型" class="headerlink" title="1.2 改进活动模型"></a>1.2 改进活动模型</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="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">PromoModel</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Integer id;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//秒杀活动状态：1表示未开始，2表示进行中，3表示已经结束</span></span><br><span class="line">    <span class="keyword">private</span> Integer status;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//秒杀活动名称</span></span><br><span class="line">    <span class="keyword">private</span> String promoName;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//秒杀活动的开始时间</span></span><br><span class="line">    <span class="keyword">private</span> DateTime startDate;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//结束时间</span></span><br><span class="line">    <span class="keyword">private</span> DateTime endDate;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//秒杀活动的适用商品</span></span><br><span class="line">    <span class="keyword">private</span> Integer itemId;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//秒杀商品的活动折扣价</span></span><br><span class="line">    <span class="keyword">private</span> BigDecimal promoItemPrice;</span><br></pre></td></tr></table></figure><ul><li>这是改进后的新模型</li><li>然后在itemModel中加入promoModel进行改进，这使用了<strong>聚合模型</strong></li><li>修改ItemVO显示对应的promo信息，这里也是聚合模型的一种体现</li></ul><h3 id="1-2-修改订单模块"><a href="#1-2-修改订单模块" class="headerlink" title="1.2 修改订单模块"></a>1.2 修改订单模块</h3><ul><li>加入一个promoId表示是否具有对应的秒杀活动。</li><li>然后对OrderService进行修改，显然第一种可能性更改，而第二种情况下平价商品也需要查询，消耗比较大。<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="comment">//1.通过前端url传入活动id，完成对于id下是否有活动？活动时间？活动是否属于商品？的校验</span></span><br><span class="line"><span class="comment">//2.直接在下单接口内判断对应商品是否存在秒杀活动，若存在进行中的则以秒杀价格下单</span></span><br><span class="line"><span class="function">OrderModel <span class="title">createOrder</span><span class="params">(Integer userId, Integer itemId, Integer amount)</span> <span class="keyword">throws</span> BusinessException</span>;</span><br></pre></td></tr></table></figure></li></ul><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><h3 id="扩展要求"><a href="#扩展要求" class="headerlink" title="扩展要求"></a>扩展要求</h3><ul><li>多商品，多库存，多活动模型怎么实现？</li><li><p>秒杀活动业务逻辑实现后，如何支撑亿级流量的实现，比如</p><ul><li>容量问题及其解决，出现在哪里，怎样压测并发现问题</li><li>系统水平扩展，怎么做到支持？redis有用吗？</li><li>查询效率低下（很深层次的问题）</li><li>活动开始前页面被疯狂刷新（查询次数会很多很大）</li><li>库存行锁问题（同一时间只有一个事务可以执行减操作，上百都成问题）</li><li>下单操作多，缓慢（策略问题）</li><li>浪涌流量如何解决（缓存瞬间失效问题）</li></ul></li></ul><h3 id="参考的解决方案"><a href="#参考的解决方案" class="headerlink" title="参考的解决方案"></a>参考的解决方案</h3><h2 id="发现问题"><a href="#发现问题" class="headerlink" title="发现问题"></a>发现问题</h2><ul><li>当前项目在完成了闭环的业务数据设计后，可以考虑系统的性能问题。现在这种spring加mybatis的框架做数据的crud存取，没有考虑扩展问题和缓存问题。</li></ul><h2 id="找到的几种解决方案"><a href="#找到的几种解决方案" class="headerlink" title="找到的几种解决方案"></a>找到的几种解决方案</h2><ul><li><p>1）考虑容器级别的优化：</p><ul><li>tomcat使用了什么样的线程池配置？</li><li>和客户端是否采用了keepalive的链接？</li><li>过载保护后的拒绝策略是什么样的？</li><li>核心线程在idle状态下需要维持多久保证可以扛住洪峰流量？</li></ul></li><li><p>2）考虑到水平扩展性：</p><ul><li>单台机器的容量无法扛住所有的压力，考虑系统是否支持无状态部署？（所谓的无状态就是没有什么配置或者数据是依赖于应用服务器的，这样才可以无脑添加机器。）</li><li>在机器得到扩展后需要有统一的存储来支持数据的访问，分布式的redis会话就可以用来解决数据会话问题。</li><li>数据库的单点问题也是可以用对应的集中式管理策略来解决</li></ul></li><li><p>3）考虑到查询压力：</p><ul><li>考虑使用多级缓存策略解决问题：怎么确保多级缓存可以有效的保障我们的系统？</li><li>怎么忍受对应的脏读问题？</li><li>一种方案：最简单的我们可以使用集中式的redis缓存方案解决问题：由于redis支持很好的cluster分片集群模式，其本身又是可以水平扩展的，当redis的网络及内存消耗不能满足热点数据访问时，我们可以使用本地自身实现lru策略的cache缓存，并控制好总容量以及提供快速的响应，并且可以将对应的热点缓存逐步靠近用户，例如可以迁移到nginx甚至于cdn上以求快速的响应用户请求。</li></ul></li><li><p>4）考虑交易压力的时候：</p><ul><li>由于交易的所有行为都依赖数据库的落地数据确保正确性，因此需要做一些数据库的锁级别的优化外，针对库存这类热点数据还可以借助缓冲等方式做实现</li><li>一旦使用了非落地的数据做交易流程，就必须确保数据的最终一致性避免超卖，超发之类的事件发生，因此通常需要设计分布式的最终一致性事务方案解决问题。</li></ul></li><li><p>5）其他问题的思考 </p><ul><li>针对过载保护，限流令牌之类的流量削峰平滑操作措施；</li><li>防刷操作</li></ul></li></ul>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;SpringBoot秒杀商城&quot;&gt;&lt;a href=&quot;#SpringBoot秒杀商城&quot; class=&quot;headerlink&quot; title=&quot;SpringBoot秒杀商城&quot;&gt;&lt;/a&gt;SpringBoot秒杀商城&lt;/h1&gt;&lt;p&gt;SpringBoot并非什么新的框架，Spr
      
    
    </summary>
    
      <category term="Java" scheme="https://github.com/TemplarJQ/categories/Java/"/>
    
      <category term="Spring" scheme="https://github.com/TemplarJQ/categories/Java/Spring/"/>
    
    
      <category term="项目笔记" scheme="https://github.com/TemplarJQ/tags/%E9%A1%B9%E7%9B%AE%E7%AC%94%E8%AE%B0/"/>
    
  </entry>
  
  <entry>
    <title>SpringBoot读取数据库乱码</title>
    <link href="https://github.com/TemplarJQ/2019/07/01/SpringBoot%E8%AF%BB%E5%8F%96%E6%95%B0%E6%8D%AE%E5%BA%93%E4%B9%B1%E7%A0%81/"/>
    <id>https://github.com/TemplarJQ/2019/07/01/SpringBoot读取数据库乱码/</id>
    <published>2019-07-01T04:17:40.000Z</published>
    <updated>2019-07-11T08:32:42.015Z</updated>
    
    <content type="html"><![CDATA[<h1 id="读写数据库中文乱码问题"><a href="#读写数据库中文乱码问题" class="headerlink" title="读写数据库中文乱码问题"></a>读写数据库中文乱码问题</h1><p>这是一个很常见的读写问题，现在需要再次整理一下具体做法。</p><h1 id="IDEA的问题"><a href="#IDEA的问题" class="headerlink" title="IDEA的问题"></a>IDEA的问题</h1><ul><li>打开setting-&gt;editor-&gt;code Style-&gt;FileEncodings，发现IDE已经为UTF-8的编码，可以排除IDE的问题。<br><img src="/2019/07/01/SpringBoot读取数据库乱码/20190711idea编码.jpg" title="idea编码"><br></li></ul><h1 id="Mysql的连接驱动"><a href="#Mysql的连接驱动" class="headerlink" title="Mysql的连接驱动"></a>Mysql的连接驱动</h1><ul><li>目前使用的连接URL为： <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">jdbc:mysql:<span class="comment">//localhost:3306/数据库?useUnicode=true&amp;amp;characterEncoding=utf-8</span></span><br></pre></td></tr></table></figure></li></ul><p>问号后面挂接的unicode编码的支持，设定为utf-8.</p><ul><li>当前很多项目的问题都是这个</li></ul><h1 id="数据库设置的问题"><a href="#数据库设置的问题" class="headerlink" title="数据库设置的问题"></a>数据库设置的问题</h1><ul><li>在设置数据库的过程中建表过程主动设置为UTF-8也是非常有作用的设置方式</li></ul><h1 id="数据库的默认设置"><a href="#数据库的默认设置" class="headerlink" title="数据库的默认设置"></a>数据库的默认设置</h1><p>这个参考数据库的具体设置，可以在默认设置里修改</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;读写数据库中文乱码问题&quot;&gt;&lt;a href=&quot;#读写数据库中文乱码问题&quot; class=&quot;headerlink&quot; title=&quot;读写数据库中文乱码问题&quot;&gt;&lt;/a&gt;读写数据库中文乱码问题&lt;/h1&gt;&lt;p&gt;这是一个很常见的读写问题，现在需要再次整理一下具体做法。&lt;/p&gt;
&lt;
      
    
    </summary>
    
      <category term="Java" scheme="https://github.com/TemplarJQ/categories/Java/"/>
    
      <category term="Spring" scheme="https://github.com/TemplarJQ/categories/Java/Spring/"/>
    
    
      <category term="项目笔记" scheme="https://github.com/TemplarJQ/tags/%E9%A1%B9%E7%9B%AE%E7%AC%94%E8%AE%B0/"/>
    
  </entry>
  
  <entry>
    <title>关于Serverless与Faas的探究</title>
    <link href="https://github.com/TemplarJQ/2019/05/11/%E5%85%B3%E4%BA%8EServerless%E4%B8%8EFaas%E7%9A%84%E6%8E%A2%E7%A9%B6/"/>
    <id>https://github.com/TemplarJQ/2019/05/11/关于Serverless与Faas的探究/</id>
    <published>2019-05-11T03:47:53.000Z</published>
    <updated>2019-07-02T03:53:47.159Z</updated>
    
    <content type="html"><![CDATA[<h1 id="关于Serverless与Faas"><a href="#关于Serverless与Faas" class="headerlink" title="关于Serverless与Faas"></a>关于Serverless与Faas</h1><p>这里的FAAS是指function as a service，主要实现有开源的OpenFaas（基于docker）与亚马逊的Lambda来实现。<br>而Serverless现有的业界处理方案还是集中在下图类似的情景下：<br>但注意，思维不要局限，这只是业界如何使用的例子，应当对如何进一步拓宽使用范围抱有信心。<br>“将业务函数抽象成一个个 FAAS 函数，将数据库、缓存、加速等服务抽象成 BAAS 服务。<br>上层提供 Restful 或事件触发机制调用，对应到不同的端（PC、移动端）。<br>想要拓展平台能力，只要在端上做开放（组件接入）与 FAAS 服务做开放（后端接入）即可。”</p><h2 id="对于现状的一种分析："><a href="#对于现状的一种分析：" class="headerlink" title="对于现状的一种分析："></a>对于现状的一种分析：</h2><p>这里挂一篇笔者的文章，参见笔记《理论 - 知乎：Serverless是进步还是退步》<br>两篇论文的地址<br><a href="https://arxiv.org/abs/1812.03651" target="_blank" rel="noopener">Serverless computing：One Step Forward，Two Steps Back</a><br><br><a href="https://www2.eecs.berkeley.edu/Pubs/TechRpts/2019/EECS-2019-3.html" target="_blank" rel="noopener">Cloud Programming Simplified：A Berkeley View on Serverless Computing</a><br></p><p>说的很清楚，现有的Serverless服务主要的一个优势和两个问题：<br>Faas是“事件驱动”的，因此需要有更好的事件驱动支持<br>优势：<br>解耦，使得服务的部署更加简单以及松耦合.</p><blockquote><p>“提供了一种弹性的，自动扩缩容的编程模式，是一大进步。”<br>劣势：<br>1）传统服务为将代码传输到数据端工作，现有服务则是将数据传输到代码端工作，即“Data-shipping”模式，忽视了数据处理的效率<br>2）阻碍了分布式计算的发展，<br>“ Faas 的函数之间不是通过网络进行端到端通信的，那么以计算机网络为基础的分布式计算理论和通信协议，例如数据一致性，leader 选举，分布式事务等的发展会受到阻碍。”</p></blockquote><h2 id="联想的另一端是互联网之父的solid项目，有其他的参考意义在其中"><a href="#联想的另一端是互联网之父的solid项目，有其他的参考意义在其中" class="headerlink" title="联想的另一端是互联网之父的solid项目，有其他的参考意义在其中"></a>联想的另一端是互联网之父的solid项目，有其他的参考意义在其中</h2><p>同时，RDF（Resource Description Framework）的理解，也有提及：即语义网</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;关于Serverless与Faas&quot;&gt;&lt;a href=&quot;#关于Serverless与Faas&quot; class=&quot;headerlink&quot; title=&quot;关于Serverless与Faas&quot;&gt;&lt;/a&gt;关于Serverless与Faas&lt;/h1&gt;&lt;p&gt;这里的FAAS是指f
      
    
    </summary>
    
      <category term="云计算" scheme="https://github.com/TemplarJQ/categories/%E4%BA%91%E8%AE%A1%E7%AE%97/"/>
    
    
      <category term="技术理论" scheme="https://github.com/TemplarJQ/tags/%E6%8A%80%E6%9C%AF%E7%90%86%E8%AE%BA/"/>
    
  </entry>
  
  <entry>
    <title>TCP粘包问题成因及其解决办法</title>
    <link href="https://github.com/TemplarJQ/2019/04/13/TCP%E7%B2%98%E5%8C%85%E9%97%AE%E9%A2%98%E6%88%90%E5%9B%A0%E5%8F%8A%E5%85%B6%E8%A7%A3%E5%86%B3%E5%8A%9E%E6%B3%95/"/>
    <id>https://github.com/TemplarJQ/2019/04/13/TCP粘包问题成因及其解决办法/</id>
    <published>2019-04-13T08:11:42.000Z</published>
    <updated>2019-07-02T04:40:07.615Z</updated>
    
    <content type="html"><![CDATA[<h1 id="研究TCP粘包问题"><a href="#研究TCP粘包问题" class="headerlink" title="研究TCP粘包问题"></a>研究TCP粘包问题</h1><p>近期和同学讨论了一个关于TCP粘包的问题，他在分布式机器学习的数据传输中发现TCP通信出现问题。因此也研究了一下自己之前项目中使用的代码 —— 是通过规定头部字段记录代码长度的方式来实现的。<br><br>这里总结一下查到的资料。<br><br><a href="https://blog.csdn.net/zhangxinrun/article/details/6721427" target="_blank" rel="noopener">引用博客</a></p><h2 id="问题的成因"><a href="#问题的成因" class="headerlink" title="问题的成因"></a>问题的成因</h2><blockquote><p>socket网络程序中，TCP和UDP分别是面向连接和非面向连接的。因此TCP的socket编程，收发两端（客户端和服务器端）都要有成对的socket，因此，发送端为了将多个发往接收端的包，更有效的发到对方，使用了优化方法（Nagle算法），将多次间隔较小、数据量小的数据，合并成一个大的数据块，然后进行封包。<br><br>这样，接收端，就难于分辨出来了，必须提供科学的拆包机制。<br>对于UDP，不会使用块的合并优化算法，这样，实际上目前认为，是由于UDP支持的是一对多的模式，所以接收端的skbuff(套接字缓冲区）采用了链式结构来记录每一个到达的UDP包，在每个UDP包中就有了消息头（消息来源地址，端口等信息），这样对于接收端来说，就容易进行区分处理了,所以UDP不会出现粘包问题。</p></blockquote><p>实际上TCP是面向流的传输协议，而UDP是面向消息的传输协议，如何保护消息边界的问题是解决粘包问题的关键。</p><h2 id="产生粘包的两种情况"><a href="#产生粘包的两种情况" class="headerlink" title="产生粘包的两种情况"></a>产生粘包的两种情况</h2><ul><li>1发送端需要等缓冲区满才发送出去，造成粘包</li><li>2接收方不及时接收缓冲区的包，造成多个包接收<blockquote><p>具体点：<br>（1）发送方引起的粘包是由TCP协议本身造成的，TCP为提高传输效率，发送方往往要收集到足够多的数据后才发送一包数据。若连续几次发送的数据都很少，通常TCP会根据优化算法把这些数据合成一包后一次发送出去，这样接收方就收到了粘包数据。<br>（2）接收方引起的粘包是由于接收方用户进程不及时接收数据，从而导致粘包现象。这是因为接收方先把收到的数据放在系统接收缓冲区，用户进程从该缓冲区取数据，若下一包数据到达时前一包数据尚未被用户进程取走，则下一包数据放到系统接收缓冲区时就接到前一包数据之后，而用户进程根据预先设定的缓冲区大小从系统接收缓冲区取数据，这样就一次取到了多包数据。<br>粘包情况有两种，一种是粘在一起的包都是完整的数据包，另一种情况是粘在一起的包有不完整的包。<br>不是所有的粘包现象都需要处理，若传输的数据为不带结构的连续流数据（如文件传输），则不必把粘连的包分开（简称分包）。但在实际工程应用中，传输的数据一般为带结构的数据，这时就需要做分包处理。<br>在处理定长结构数据的粘包问题时，分包算法比较简单；在处理不定长结构数据的粘包问题时，分包算法就比较复杂。特别是粘在一起的包有不完整的包的粘包情况，由于一包数据内容被分在了两个连续的接收包中，处理起来难度较大。实际工程应用中应尽量避免出现粘包现象。</p></blockquote></li></ul><h2 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h2><ul><li>(1)发送固定长度的消息</li><li>(2)把消息的尺寸与消息一块发送</li><li>(3)使用特殊标记来区分消息间隔</li></ul><h2 id="之前的代码"><a href="#之前的代码" class="headerlink" title="之前的代码"></a>之前的代码</h2><p>之前的一种限定长度的代码为：<br></p><p>发送：<br><br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@classmethod</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">encode_socket_data</span><span class="params">(cls, data: object)</span> -&gt; bytes:</span></span><br><span class="line">        <span class="string">"""Our protocol is: first 4 bytes signify msg length."""</span></span><br><span class="line">        <span class="function"><span class="keyword">def</span> <span class="title">int_to_8bytes</span><span class="params">(a: int)</span> -&gt; bytes:</span></span><br><span class="line">            <span class="keyword">return</span> binascii.unhexlify(<span class="string">f"<span class="subst">&#123;a:<span class="number">0</span>&#123;<span class="number">8</span>&#125;</span>x&#125;"</span>)</span><br><span class="line">        to_send = Utils.serialize(data).encode()</span><br><span class="line">        <span class="keyword">return</span> int_to_8bytes(len(to_send)) + to_send</span><br></pre></td></tr></table></figure></p><p>接收：<br><br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span>:</span><br><span class="line">            <span class="keyword">with</span> socket.socket(socket.AF_INET, socket.SOCK_STREAM) <span class="keyword">as</span> s:</span><br><span class="line">                s.connect(peer())</span><br><span class="line">                s.sendall(Utils.encode_socket_data(message))</span><br><span class="line">                logger.info(<span class="string">f'[p2p] succeed to send BlocksSyncReq to <span class="subst">&#123;peer&#125;</span>'</span>)</span><br><span class="line">                msg_len = int(binascii.hexlify(s.recv(<span class="number">4</span>) <span class="keyword">or</span> <span class="string">b'\x00'</span>), <span class="number">16</span>)</span><br><span class="line">                data = <span class="string">b''</span></span><br><span class="line">                <span class="keyword">while</span> msg_len &gt; <span class="number">0</span>:</span><br><span class="line">                    tdat = s.recv(<span class="number">1024</span>)</span><br><span class="line">                    data += tdat</span><br><span class="line">                    msg_len -= len(tdat)</span><br><span class="line">            s.close()</span><br></pre></td></tr></table></figure></p><p>规定开头四字节的长度即可在缓冲区中区分TCP数据包。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;研究TCP粘包问题&quot;&gt;&lt;a href=&quot;#研究TCP粘包问题&quot; class=&quot;headerlink&quot; title=&quot;研究TCP粘包问题&quot;&gt;&lt;/a&gt;研究TCP粘包问题&lt;/h1&gt;&lt;p&gt;近期和同学讨论了一个关于TCP粘包的问题，他在分布式机器学习的数据传输中发现TCP通
      
    
    </summary>
    
      <category term="计算机网络" scheme="https://github.com/TemplarJQ/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/"/>
    
    
      <category term="技术理论" scheme="https://github.com/TemplarJQ/tags/%E6%8A%80%E6%9C%AF%E7%90%86%E8%AE%BA/"/>
    
  </entry>
  
  <entry>
    <title>P2P网络中内网穿透的方法</title>
    <link href="https://github.com/TemplarJQ/2019/01/22/P2P%E7%BD%91%E7%BB%9C%E4%B8%AD%E5%86%85%E7%BD%91%E7%A9%BF%E9%80%8F%E7%9A%84%E6%96%B9%E6%B3%95/"/>
    <id>https://github.com/TemplarJQ/2019/01/22/P2P网络中内网穿透的方法/</id>
    <published>2019-01-22T13:11:00.000Z</published>
    <updated>2019-01-22T13:52:21.879Z</updated>
    
    <content type="html"><![CDATA[<h1 id="P2P网络中内网穿透的方法"><a href="#P2P网络中内网穿透的方法" class="headerlink" title="P2P网络中内网穿透的方法"></a>P2P网络中内网穿透的方法</h1><p>今天学长让我查一下这个，研究了一下，各种资料五花八门，自己总结了一下备查。<br><br>是关于一个局域网和外网之间连接的问题:<br><br><br><br><br><br>首先问题的关键在于：<br><br><img src="/2019/01/22/P2P网络中内网穿透的方法/resolution1.jpg" title="问题分析"><br></p><p>由于动态NAPT的映射关系是LAN侧数据包来触发的，如果WAN侧有主动进来的数据包，因为查询不到映射关系的存在，就会被丢弃掉。所以这时需要内网穿透。<br></p><h2 id="方案一"><a href="#方案一" class="headerlink" title="方案一"></a>方案一<br></h2><p>理念是构建一个公网中的服务端，两个内网中的客户端都相当于向该服务器注册，然后发送三次握手，可互相发现，实现NAT穿透。<br>Python写的比较常用的实现TCP内网穿透的服务器是ShootBack：<br><br><img src="/2019/01/22/P2P网络中内网穿透的方法/resolution2.jpg" title="tcp"><br></p><h2 id="方案二"><a href="#方案二" class="headerlink" title="方案二"></a>方案二<br></h2><p>另一个比较简单的方式是：端口映射(Port Mapping或者叫Port Forwarding)，也是最基本的一种方式。<br><br>它将NAPT网关WAN侧的指定端口映射到内网指定地址的指定端口上。 这样当网关收到一个从外网过来的封包，就会转发到上述指定的内网地址和端口，对于封包的发起者来说，就像是直接访问这个内网主机一样。<br><br>且当内网的地址和端口都希望是动态的时候就需要“动态端口映射”，也就是UPnP里面的IGD（Internet Gateway Device）控制协议。<br>这种方式下的情形就是： 把节点连接在某个UPnP分配的节点上，然后外部直接来连接这个端口。<br><br>现在了解到的python下分离并加强这种分配端口方法的的包就是miniUpnp，还有待于了解；<br><br>但这种方法存在的问题：<br><br>——使用UPNP穿透NAT的方法会使得机器不安全，极其容易卡死路由器和其他问题；<br><br>——最初是为各种设备，后来发展为一般都是为电驴等软件配置某一个外部端口的时候开启，而自己写的某种程序能否做到端口映射问题很大，且开源多基于C；<br><br>——这种方法受到不同网络状况和设备的影响很大；<br><br>——多用于家庭内的树莓派或者设备连接外部公司里电脑这种情况；<br></p><h2 id="方案三"><a href="#方案三" class="headerlink" title="方案三"></a>方案三<br></h2><p>STUN协议和TURN协议<br><br>STUN（Simple Traversal of UDP over NATs，NAT 的UDP简单穿越）是一种网络协议，它允许位于NAT（或多重NAT）后的客户端找出自己的公网地址，查出自己位于哪种类型的NAT之后以及NAT为某一 个本地端口所绑定的Internet端端口。这些信息被用来在两个同时处于NAT路由器之后的主机之间建立UDP通信。<br><br><img src="/2019/01/22/P2P网络中内网穿透的方法/resolution.jpg" title="stun"><br><br>如图，在N层NAT后面的client向Server询问自己的外网IP和端口号，然后直接用于连接，这样就可以直接使用外部端口和IP，避免使用私网IP和端口无法被访问的问题。<br><br>网上开源项目也很多：<br><br>显然pystun支持的是python2.7之前的版本，现在版本需要的是pynat。<br><br><img src="/2019/01/22/P2P网络中内网穿透的方法/resolution4.jpg" title="pynat"><br></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;P2P网络中内网穿透的方法&quot;&gt;&lt;a href=&quot;#P2P网络中内网穿透的方法&quot; class=&quot;headerlink&quot; title=&quot;P2P网络中内网穿透的方法&quot;&gt;&lt;/a&gt;P2P网络中内网穿透的方法&lt;/h1&gt;&lt;p&gt;今天学长让我查一下这个，研究了一下，各种资料五花八门
      
    
    </summary>
    
      <category term="计算机网络" scheme="https://github.com/TemplarJQ/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/"/>
    
    
      <category term="技术总结" scheme="https://github.com/TemplarJQ/tags/%E6%8A%80%E6%9C%AF%E6%80%BB%E7%BB%93/"/>
    
  </entry>
  
  <entry>
    <title>VirtualEnv常用命令</title>
    <link href="https://github.com/TemplarJQ/2019/01/14/VirtualEnv%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4/"/>
    <id>https://github.com/TemplarJQ/2019/01/14/VirtualEnv常用命令/</id>
    <published>2019-01-14T08:40:53.000Z</published>
    <updated>2019-01-14T10:56:27.580Z</updated>
    
    <content type="html"><![CDATA[<h1 id="最近python用的多"><a href="#最近python用的多" class="headerlink" title="最近python用的多"></a>最近python用的多</h1><p>Pycharm的基本配置和记录中Virtualenv的使用都记录在这里。<br><br><a href="https://www.cnblogs.com/geeklove01/p/8030214.html" target="_blank" rel="noopener">基本配置</a><br><br>再有这是virtualenv的所有基础操作<br><br><a href="https://blog.csdn.net/makodoo/article/details/79228692" target="_blank" rel="noopener">基础操作</a><br></p><p></p><p><br>自己Mac的virtualenv环境在：~/.virtualenv文件夹下面。<br><br>一些常见的virtual命令：<br><br><img src="/2019/01/14/VirtualEnv常用命令/virtualenv命令.jpg" title="virtualenv常见命令"><br> </p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;最近python用的多&quot;&gt;&lt;a href=&quot;#最近python用的多&quot; class=&quot;headerlink&quot; title=&quot;最近python用的多&quot;&gt;&lt;/a&gt;最近python用的多&lt;/h1&gt;&lt;p&gt;Pycharm的基本配置和记录中Virtualenv的使用都记录在这
      
    
    </summary>
    
      <category term="Python" scheme="https://github.com/TemplarJQ/categories/Python/"/>
    
    
      <category term="技术链接" scheme="https://github.com/TemplarJQ/tags/%E6%8A%80%E6%9C%AF%E9%93%BE%E6%8E%A5/"/>
    
  </entry>
  
  <entry>
    <title>日志模板</title>
    <link href="https://github.com/TemplarJQ/2019/01/14/%E6%97%A5%E5%BF%97%E6%A8%A1%E6%9D%BF/"/>
    <id>https://github.com/TemplarJQ/2019/01/14/日志模板/</id>
    <published>2019-01-14T08:11:13.000Z</published>
    <updated>2019-01-14T10:51:09.820Z</updated>
    
    <content type="html"><![CDATA[<!-- 引言 --><blockquote><footer><strong>XX</strong><cite>XX</cite></footer></blockquote><hr><!-- 引入图片 --><img src="/2019/01/14/日志模板/jfla.jpg" title="测试"><br><br><br># 一级标题<br><br><em>斜体文字</em><br><strong>加粗文字</strong><br><strong><em>粗斜体文字</em></strong><br><br><blockquote class="blockquote-center"><p> 文本居中的引用<br>测试<br>测试2  </p></blockquote><!-- 分割线 --><hr><hr><!-- 行内代码 --><p><code>hello world</code><br><code>for(int i=0;i&lt;len;i++){</code><br>    <code>step++;</code><br><code>}</code></p><!-- 灰色底板 --><div class="note class_name">            <p>文字内容 (md class_name supported) </p>          </div><!-- 紫色 --><div class="note primary">            <p>文字内容 (md primary supported) </p>          </div><!-- 深灰 --><div class="note default">            <p>文字内容 (md default supported) </p>          </div><!-- 绿色 --><div class="note success">            <p>文字内容 (md success supported) </p>          </div><!-- 蓝色 --><div class="note info">            <p>文字内容 (md info supported) </p>          </div><!-- 黄色 --><div class="note warning">            <p>文字内容 (md warning supported) </p>          </div><!-- 红色 --><div class="note danger">            <p>文字内容 (md danger supported) </p>          </div><!-- 待办事项 --><ul><li style="list-style: none"><input type="checkbox"> 支持以 PDF 格式导出文稿</li><li style="list-style: none"><input type="checkbox"> 改进 Cmd 渲染算法，使用局部渲染技术提高渲染效率</li><li style="list-style: none"><input type="checkbox" checked> 新增 Todo 列表功能</li></ul>]]></content>
    
    <summary type="html">
    
      
      
        &lt;!-- 引言 --&gt;
&lt;blockquote&gt;&lt;footer&gt;&lt;strong&gt;XX&lt;/strong&gt;&lt;cite&gt;XX&lt;/cite&gt;&lt;/footer&gt;&lt;/blockquote&gt;
&lt;hr&gt;

&lt;!-- 引入图片 --&gt;
&lt;img src=&quot;/2019/01/14/日志模板/jfla
      
    
    </summary>
    
      <category term="日志" scheme="https://github.com/TemplarJQ/categories/%E6%97%A5%E5%BF%97/"/>
    
    
      <category term="日志" scheme="https://github.com/TemplarJQ/tags/%E6%97%A5%E5%BF%97/"/>
    
      <category term="心情" scheme="https://github.com/TemplarJQ/tags/%E5%BF%83%E6%83%85/"/>
    
  </entry>
  
  <entry>
    <title>众包项目</title>
    <link href="https://github.com/TemplarJQ/2019/01/13/%E4%BC%97%E5%8C%85%E9%A1%B9%E7%9B%AE/"/>
    <id>https://github.com/TemplarJQ/2019/01/13/众包项目/</id>
    <published>2019-01-13T13:51:55.000Z</published>
    <updated>2019-01-22T13:52:24.217Z</updated>
    
    <content type="html"><![CDATA[<h1 id="边缘群智项目记录"><a href="#边缘群智项目记录" class="headerlink" title="边缘群智项目记录"></a>边缘群智项目记录</h1><p>2019.1.13打卡，明天下午一点开会，开始搞这个，记录一下流程。<hr><br><a href="https://github.com/EdgeIntelligenceChain/Materials2Study">众包相关知识</a><br><br><a href="https://github.com/EdgeIntelligenceChain/EdgenceChain/issues/6">快速代码上手</a><br></p><h2 id="项目入门"><a href="#项目入门" class="headerlink" title="项目入门"></a>项目入门</h2><p>项目介绍图，是一些学长的资料<br><br><img src="/2019/01/13/众包项目/introToCrowdsourcing.jpg" title="众包简介"><br><br><!-- ![众包简介](众包项目/introToCrowdsourcing.jpg) --></p><h2 id="第一个研究问题"><a href="#第一个研究问题" class="headerlink" title="第一个研究问题"></a>第一个研究问题</h2><p>是关于一个局域网和外网之间连接的问题:<br><br><img src="/2019/01/13/众包项目/question.jpg" title="问题简介"><br><br><br><br>首先问题的关键在于：<br><br><img src="/2019/01/13/众包项目/resolution1.jpg" title="问题分析"><br></p><p>由于动态NAPT的映射关系是LAN侧数据包来触发的，如果WAN侧有主动进来的数据包，因为查询不到映射关系的存在，就会被丢弃掉。所以这时需要内网穿透。<br></p><h3 id="方案一"><a href="#方案一" class="headerlink" title="方案一"></a>方案一<br></h3><p>理念是构建一个公网中的服务端，两个内网中的客户端都相当于向该服务器注册，然后发送三次握手，可互相发现，实现NAT穿透。<br>Python写的比较常用的实现TCP内网穿透的服务器是ShootBack：<br><br><img src="/2019/01/13/众包项目/resolution2.jpg" title="tcp"><br></p><h3 id="方案二"><a href="#方案二" class="headerlink" title="方案二"></a>方案二<br></h3><p>另一个比较简单的方式是：端口映射(Port Mapping或者叫Port Forwarding)，也是最基本的一种方式。<br><br>它将NAPT网关WAN侧的指定端口映射到内网指定地址的指定端口上。 这样当网关收到一个从外网过来的封包，就会转发到上述指定的内网地址和端口，对于封包的发起者来说，就像是直接访问这个内网主机一样。<br><br>且当内网的地址和端口都希望是动态的时候就需要“动态端口映射”，也就是UPnP里面的IGD（Internet Gateway Device）控制协议。<br>这种方式下的情形就是： 把节点连接在某个UPnP分配的节点上，然后外部直接来连接这个端口。<br><br>现在了解到的python下分离并加强这种分配端口方法的的包就是miniUpnp，还有待于了解；<br><br>但这种方法存在的问题：<br><br>——使用UPNP穿透NAT的方法会使得机器不安全，极其容易卡死路由器和其他问题；<br><br>——最初是为各种设备，后来发展为一般都是为电驴等软件配置某一个外部端口的时候开启，而自己写的某种程序能否做到端口映射问题很大，且开源多基于C；<br><br>——这种方法受到不同网络状况和设备的影响很大；<br><br>——多用于家庭内的树莓派或者设备连接外部公司里电脑这种情况；<br></p><h3 id="方案三"><a href="#方案三" class="headerlink" title="方案三"></a>方案三<br></h3><p>STUN协议和TURN协议<br><br>STUN（Simple Traversal of UDP over NATs，NAT 的UDP简单穿越）是一种网络协议，它允许位于NAT（或多重NAT）后的客户端找出自己的公网地址，查出自己位于哪种类型的NAT之后以及NAT为某一 个本地端口所绑定的Internet端端口。这些信息被用来在两个同时处于NAT路由器之后的主机之间建立UDP通信。<br><br><img src="/2019/01/13/众包项目/resolution.jpg" title="stun"><br><br>如图，在N层NAT后面的client向Server询问自己的外网IP和端口号，然后直接用于连接，这样就可以直接使用外部端口和IP，避免使用私网IP和端口无法被访问的问题。<br><br>网上开源项目也很多：<br><br>显然pystun支持的是python2.7之前的版本，现在版本需要的是pynat。<br><br><img src="/2019/01/13/众包项目/resolution4.jpg" title="pynat"><br></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;边缘群智项目记录&quot;&gt;&lt;a href=&quot;#边缘群智项目记录&quot; class=&quot;headerlink&quot; title=&quot;边缘群智项目记录&quot;&gt;&lt;/a&gt;边缘群智项目记录&lt;/h1&gt;&lt;p&gt;2019.1.13打卡，明天下午一点开会，开始搞这个，记录一下流程。&lt;hr&gt;&lt;br&gt;&lt;a h
      
    
    </summary>
    
      <category term="区块链" scheme="https://github.com/TemplarJQ/categories/%E5%8C%BA%E5%9D%97%E9%93%BE/"/>
    
      <category term="众包" scheme="https://github.com/TemplarJQ/categories/%E5%8C%BA%E5%9D%97%E9%93%BE/%E4%BC%97%E5%8C%85/"/>
    
    
      <category term="项目笔记" scheme="https://github.com/TemplarJQ/tags/%E9%A1%B9%E7%9B%AE%E7%AC%94%E8%AE%B0/"/>
    
  </entry>
  
  <entry>
    <title>机器学习和大数据资料整理</title>
    <link href="https://github.com/TemplarJQ/2019/01/13/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E5%92%8C%E5%A4%A7%E6%95%B0%E6%8D%AE%E9%93%BE%E6%8E%A5/"/>
    <id>https://github.com/TemplarJQ/2019/01/13/机器学习和大数据链接/</id>
    <published>2019-01-13T13:06:16.000Z</published>
    <updated>2019-01-14T09:45:06.195Z</updated>
    
    <content type="html"><![CDATA[<h1 id="机器学习相关技术链接"><a href="#机器学习相关技术链接" class="headerlink" title="机器学习相关技术链接"></a>机器学习相关技术链接</h1><p>贴一些常用的链接就不到Chorme tabs里找了。</p><h2 id="机器学习"><a href="#机器学习" class="headerlink" title="机器学习"></a>机器学习</h2><div class="note success">            <p><a href="https://drive.google.com/drive/my-drive" target="_blank" rel="noopener">我的google云盘</a><br><br><a href="https://www.bilibili.com/video/av9912938/?p=1" target="_blank" rel="noopener">吴恩达b站视频</a><br><br><a href="https://redstonewill.com/category/ai-notes/lin-ml-foundations/" target="_blank" rel="noopener">林轩石机器学习</a><br><br><a href="https://www.bilibili.com/video/av16001891/?p=28" target="_blank" rel="noopener">莫烦的TF教程</a><br><br><a href="https://blog.csdn.net/icefire_tyh/article/details/52064910" target="_blank" rel="noopener">西瓜书答案</a><br><br><a href="http://www.xuyankun.cn/2017/05/13/bayes/" target="_blank" rel="noopener">贝叶斯讲解</a><br><br><a href="https://github.com/ctgk/PRML">PRML书目</a><br></p>          </div><!-- []()<br> --><h2 id="大数据"><a href="#大数据" class="headerlink" title="大数据"></a>大数据</h2><div class="note success">            <p><a href="http://cs231n.github.io/" target="_blank" rel="noopener">CS231n: Convolutional Neural Networks for Visual Recognition</a><br><br><a href="https://courses.edx.org/courses/BerkeleyX/CS190.1x/1T2015/course/" target="_blank" rel="noopener">Scalable Machine Learning</a><br><br><a href="https://courses.edx.org/courses/course-v1:BerkeleyX+CS105x+1T2016/course/" target="_blank" rel="noopener">Introduction to Apache Spark</a><br></p>          </div>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;机器学习相关技术链接&quot;&gt;&lt;a href=&quot;#机器学习相关技术链接&quot; class=&quot;headerlink&quot; title=&quot;机器学习相关技术链接&quot;&gt;&lt;/a&gt;机器学习相关技术链接&lt;/h1&gt;&lt;p&gt;贴一些常用的链接就不到Chorme tabs里找了。&lt;/p&gt;
&lt;h2 id=
      
    
    </summary>
    
      <category term="机器学习" scheme="https://github.com/TemplarJQ/categories/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
    
    
      <category term="技术链接" scheme="https://github.com/TemplarJQ/tags/%E6%8A%80%E6%9C%AF%E9%93%BE%E6%8E%A5/"/>
    
  </entry>
  
</feed>
