<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>ZOU</title>
  
  
  <link href="https://goodzou.github.io/atom.xml" rel="self"/>
  
  <link href="https://goodzou.github.io/"/>
  <updated>2026-04-20T16:02:02.485Z</updated>
  <id>https://goodzou.github.io/</id>
  
  <author>
    <name>zou</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>linux文本编辑器</title>
    <link href="https://goodzou.github.io/2026/0420/"/>
    <id>https://goodzou.github.io/2026/0420/</id>
    <published>2026-04-20T12:00:00.000Z</published>
    <updated>2026-04-20T16:02:02.485Z</updated>
    
    <content type="html"><![CDATA[<p>Vim 是从 vi 发展出来的一个文本编辑器。代码补全、编译及错误跳转等方便编程的功能特别丰富，在开发运维中被广泛使用。<span id="more"></span></p><h3 id="vim-的使用"><a href="#vim-的使用" class="headerlink" title="vim 的使用"></a>vim 的使用</h3><p>基本上 vi&#x2F;vim 共分为三种模式，命令模式（Command Mode）、输入模式（Insert Mode）和命令行模式（Command-Line Mode）。</p><h4 id="命令模式"><a href="#命令模式" class="headerlink" title="命令模式"></a>命令模式</h4><p>用户刚刚启动 vi&#x2F;vim，便进入了命令模式。此状态下敲击键盘动作会被 Vim 识别为命令，而非输入字符，比如我们此时按下 <strong>i</strong>，并不会输入一个字符，<strong>i</strong> 被当作了一个命令。</p><p>以下是普通模式常用的几个命令：</p><ul><li><strong>i</strong>：切换到输入模式，在光标当前位置开始输入文本。</li><li><strong>x</strong> ： 删除当前光标所在处的字符。</li><li><strong>:</strong> ：切换到底线命令模式，以在最底一行输入命令。</li><li><strong>a</strong> ： 进入插入模式，在光标下一个位置开始输入文本。</li><li><strong>o</strong>：在当前行的下方插入一个新行，并进入插入模式。</li><li><strong>O</strong> ： 在当前行的上方插入一个新行，并进入插入模式。</li><li><strong>dd</strong> ： 剪切当前行。</li><li><strong>yy</strong> ： 复制当前行。</li><li><strong>p</strong>（小写）：粘贴剪贴板内容到光标下方。</li><li><strong>P</strong>（大写）：粘贴剪贴板内容到光标上方。</li><li><strong>u</strong> ：撤销上一次操作。</li><li><strong>Ctrl + r</strong> ：重做上一次撤销的操作。</li><li><strong>:w</strong> ：保存文件。</li><li><strong>:q</strong> ：退出 Vim 编辑器。</li><li><strong>:q!</strong> ：强制退出Vim 编辑器，不保存修改。</li></ul><p>若想要编辑文本，只需要启动 Vim，进入了命令模式，按下 <strong>i</strong> 切换到输入模式即可。命令模式只有一些最基本的命令，因此仍要依靠底线命令行模式输入更多命令。</p><p><strong>光标移动</strong></p><ul><li><strong>Ctrl + f</strong>：屏幕向下移动一页，相当于Page Down按键 </li><li><strong>Ctrl + b</strong>：屏幕向上移动一页，相当于Page Up按键 </li><li><strong>0或功能键Home</strong>：这是数字0 ：移动到这一行的最前面字符处</li><li><strong>$或功能键End</strong>：移动到这一行的最后面字符处</li><li><strong>G</strong>：移动到这个档案的最后一行</li><li><strong>gg</strong>：移动到这个档案的第一行，相当于1G</li><li><strong>n Enter</strong>：n 为数字。光标向下移动n行</li><li><strong>n G</strong>：n 为数字。移动到这个档案的第n行。例如 20G 则会移动到第20行(可配合 :set nu)，也可输入20,然后<code>shift+g</code>。</li></ul><p><strong>复制粘贴</strong></p><ul><li><p><strong>x</strong>, <strong>X</strong>：在一行字当中，x 为向后删除一个字符， X为向前删除一个字符</p></li><li><p><strong>nx</strong>：n 为数字，连续向后删除 n 个字符。举例来说，连续删除10个字符，10x</p></li><li><p><strong>dd</strong>：剪切游标所在的那一整行，用 p&#x2F;P 可以粘贴。</p></li><li><p><strong>ndd</strong>：n 为数字。剪切光标所在的向下 n 行，例如 20dd 则是剪切20行，用p&#x2F;P可以粘贴。</p></li><li><p><strong>dG</strong>：删除光标所在到最后一行的所有数据</p></li><li><p><strong>yy</strong>：复制游标所在的那一行</p></li><li><p><strong>nyy</strong>：n 为数字。复制光标所在的向下 n 行，例如 20yy 则是复制 20 行</p></li><li><p><strong>u</strong>：撤销，复原前一个动作</p></li><li><p><strong>[Ctrl]+r</strong>：重做上一个动作</p></li><li><p><strong><code>.</code></strong>：点，重复前一个动作的意思。 </p></li><li><p><strong>p</strong>, <strong>P</strong>：p 为将已复制的数据在光标下一行贴上，P 则为贴在游标上一行。举例来说，目前光标在第 20 行，且已经复制了 10 行数据。则按下 p 后， 那 10 行数据会贴在原本的 20 行之后，亦即由 21 行开始贴。但如果是按下 P 呢？ 那么原本的第 20 行会被推到变成 30 行</p><p>其他：</p></li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">d1G删除光标所在到第一行的所有数据</span><br><span class="line">d$删除游标所在处，到该行的最后一个字符</span><br><span class="line">d0那个是数字的 0 ，删除游标所在处，到该行的最前面一个字符</span><br><span class="line">y1G复制游标所在行到第一行的所有数据</span><br><span class="line">yG复制游标所在行到最后一行的所有数据</span><br><span class="line">y0复制光标所在的那个字符到该行行首的所有数据</span><br><span class="line">y$复制光标所在的那个字符到该行行尾的所有数据</span><br><span class="line">J将光标所在行与下一行的数据结合成同一行</span><br><span class="line">c重复删除多个数据，例如向下删除10行，10cj</span><br></pre></td></tr></table></figure><p><strong>搜索替换</strong></p><ul><li><p><code>/word</code>：查找单词word，命令模式下&#x2F;单词，回车，<code>n</code>查找下一个，<code>N</code>上一个</p></li><li><p><code>:n1,n2s/word1/word2/g</code>：n1 与 n2 为数字。在第 n1 与 n2 行之间寻找 word1 这个字符串，并将该字符串取代为 word2</p></li><li><p><code>:1,$s/word1/word2/g</code> 或 <code>:%s/word1/word2/g</code>：从第一行到最后一行寻找 word1 字符串，并将该字符串取代为 word2 </p></li><li><p><code>:1,$s/word1/word2/gc</code> 或 <code>:%s/word1/word2/gc</code>：从第一行到最后一行寻找 word1 字符串，并将该字符串取代为 word2，且在取代前显示提示字符给用户确认 (confirm) 是否需要取代。</p></li></ul><h4 id="输入模式"><a href="#输入模式" class="headerlink" title="输入模式"></a>输入模式</h4><p>在命令模式下按下 <strong>i</strong> 就进入了输入模式，使用 <strong>Esc</strong> 键可以返回到普通模式。</p><ul><li><strong>i</strong>，**I **进入输入模式(Insert mode)： <code>i</code> 为从目前光标所在处输入， <code>I </code>为在目前所在行的第一个非空格符处开始输入</li><li><strong>a</strong>, **A **进入输入模式(Insert mode)： <code>a</code> 为从目前光标所在的下一个字符处开始输入， <code>A</code> 为从光标所在行的最后一个字符处开始输入。</li><li><strong>o</strong>, **O **进入输入模式(Insert mode)： 这是英文字母 <code>o</code> 的大小写。<code>o </code>为在目前光标所在的下一行处输入新的一行， <code>O</code> 为在目前光标所在的上一行处输入新的一行！</li><li><strong>r</strong>, <strong>R</strong> 进入替换模式(Replace mode)： <code>r</code> 只会替换光标所在的那一个字符一次，<code>R</code>会一直替换光标所在的文字，直到按下 ESC 为止</li></ul><p>在输入模式中，可以使用以下按键：</p><ul><li>字符按键以及Shift组合，输入字符</li><li>ENTER，回车键，换行</li><li>BACK SPACE，退格键，删除光标前一个字符</li><li>DEL，删除键，删除光标后一个字符</li><li>方向键，在文本中移动光标</li><li>HOME&#x2F;END，移动光标到行首&#x2F;行尾</li><li>Page Up&#x2F;Page Down，上&#x2F;下翻页</li><li>Insert，切换光标为输入&#x2F;替换模式，光标将变成竖线&#x2F;下划线</li><li>ESC，退出输入模式，切换到命令模式</li></ul><h4 id="底线命令模式"><a href="#底线命令模式" class="headerlink" title="底线命令模式"></a>底线命令模式</h4><p>在命令模式下按下 <strong>:</strong>（英文冒号）就进入了底线命令模式。底线命令模式可以输入单个或多个字符的命令，可用的命令非常多。在底线命令模式中，基本的命令有：</p><ul><li><code>:w</code>：保存文件。</li><li><code>:q</code>：退出 Vim 编辑器。</li><li><code>:wq</code>：保存文件并退出 Vim 编辑器。</li><li><code>:q!</code>：强制退出Vim编辑器，不保存修改。</li><li><code>:set nu</code>：显示行号，设定之后，会在每一行的前缀显示该行的行号。</li><li><code>:set nonu</code>：取消行号。</li></ul><p>按 <code>ESC</code> 键可随时退出底线命令模式。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;Vim 是从 vi 发展出来的一个文本编辑器。代码补全、编译及错误跳转等方便编程的功能特别丰富，在开发运维中被广泛使用。</summary>
    
    
    
    <category term="linux" scheme="https://goodzou.github.io/categories/linux/"/>
    
    
  </entry>
  
  <entry>
    <title>SpringBoot原理解析</title>
    <link href="https://goodzou.github.io/2024/0118/"/>
    <id>https://goodzou.github.io/2024/0118/</id>
    <published>2024-01-18T01:16:09.000Z</published>
    <updated>2026-02-11T15:25:10.960Z</updated>
    
    <content type="html"><![CDATA[<p>springboot是一种简化springweb开发的框架，类似springmvc，他提供各种默认配置，达到开箱即用、敏捷开发的效果。本文主要介绍springboot的依赖管理、自动配置、web开发、thymeleaf与视图解析、拦截器、文件上传、异常处理、web原生组件注入、整合数据源Druid与Redis、Junit单元测试、spring Acutuator性能监控、高级特性与springboot启动原理。<span id="more"></span></p><h4 id="依赖管理"><a href="#依赖管理" class="headerlink" title="依赖管理"></a>依赖管理</h4><h5 id="父项目依赖"><a href="#父项目依赖" class="headerlink" title="父项目依赖"></a>父项目依赖</h5><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">parent</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-parent<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>2.7.1<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;<span class="name">relativePath</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">parent</span>&gt;</span></span><br></pre></td></tr></table></figure><p>作用：依赖管理及版本管理。</p><p>父项目的父项目：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">parent</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-dependencies<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>2.7.1<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">parent</span>&gt;</span></span><br></pre></td></tr></table></figure><p>其中默认引入了许多依赖，自动版本仲裁无需关注版本，我们可以不用配置某些依赖的版本，但是第三方需要。根据maven就近原则，默认使用本项目pom.xml配置好的依赖，若需要修改依赖版本号：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">properties</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;<span class="name">java.version</span>&gt;</span>8<span class="tag">&lt;/<span class="name">java.version</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;<span class="name">mysql.version</span>&gt;</span>5.2.41<span class="tag">&lt;/<span class="name">mysql.version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">properties</span>&gt;</span></span><br></pre></td></tr></table></figure><h5 id="starter场景启动器"><a href="#starter场景启动器" class="headerlink" title="starter场景启动器"></a>starter场景启动器</h5><p>starter也叫依赖管理器，spring-boot-starter-*是用来开发某一场景的一组依赖，引入starter就导入了相关的开发依赖。</p><p>starter基本依赖为pring-boot-starter，web开发还需要引入spring-boot-starter-json、spring-boot-starter-tomcat、spring-web、spring-webmvc。</p><h4 id="自动配置"><a href="#自动配置" class="headerlink" title="自动配置"></a>自动配置</h4><p>启动时加载所有自动配置和组件，条件装配按需配置。自动配置tomcat、自动配置springMVC、自动配置web常见功能、自动扫描springboot主程序所在包及子包下的bean组件。</p><p><code>@SpringBootApplication</code>注解相当于<code>@SpringBootConfiguration</code>、<code>@EnableAutoConfiguration</code>、<code>@ComponentScan</code>注解。</p><p>自动配置按需加载，pom.xml引入依赖后该依赖的自动配置才会生效。配置会映射到某个配置类中，springboot默认配置会映射到名为xxProperties.class配置类中，配置类也是组件。</p><p><code>@Configuration</code>声明配置类，参数proxyBeanMethods默认为true，开启代理，即使用cglib生成代理对象，只会在容器中生成一个组件示例。若为false，则不会生成代理对象，每次调用组件都会创建一个实例对象。</p><p><code>@Bean</code>给容器中添加组件，以类名或方法名做id，返回类型为组件类型，返回值即组件在容器中的示例。<code>@Bean</code>注解的方法名默认作为对象的名字，也可以用name属性定义对象的名字。<code>@bean</code>分为两种模式,一种是Lite Mode(轻量模式),这种模式下被<code>@bean</code>定义的方法需要在<code>@Component</code>下或者原生类下,效果类似于<code>@Component</code>注册在类上的效果。<code>@Configuration</code>下的<code>@bean</code>被称为是Full mode，bean的创建是通过cglib代理生成的被<code>@Configuration</code>定义的类的增强类，因为<code>@Configuration</code>定义的类的bean，默认都是被Spring通过Cglib增强的子类。</p><p><code>@Bean</code>注解参数：</p><p>value：定义bean在IOC容器中的id属性。<br>name ：定义bean在IOC容器中的id属性。<br>autowire：装配方式<br>Autowire.NO (默认设置)<br>Autowire.BY_NAME<br>Autowire.BY_TYPE<br>initMethod：指定初始化方法 相当于xml文件中 init-method<br>destroyMethod：指定销毁的方法 相当于xml文件中 destroy-method</p><h5 id="Bean和-Component的区别："><a href="#Bean和-Component的区别：" class="headerlink" title="@Bean和@Component的区别："></a><u><code>@Bean</code>和<code>@Component</code>的区别：</u></h5><p><code>@Bean</code>和<code>@Component</code>都是将Spring Bean添加到Spring Context 中。</p><p>1）作用域</p><p><code>@Component</code>注解表明一个类会作为组件类，并告知 Spring 要为这个类创建 bean。<code>@Bean</code>不能作用在类上，只能作用于方法。</p><p><code>@Bean</code>注解告诉 Spring 这个方法将会返回一个对象，这个对象要注册为 Spring 应用上下文中的 bean。要获取这个 bean 的时候，Spring 要按照这种方式、去获取这个 bean。</p><p>2）注册方式</p><p><code>@Component</code>注解表明一个类会作为组件类，并告知 Spring 要为这个类创建 bean。<code>@Bean</code>注解告诉 Spring 这个方法将会返回一个对象，这个对象要注册为 Spring 应用上下文中的 bean。通常方法体中包含了最终产生bean实例的逻辑。</p><p>当我们引用第三方库中的类需要装配到 Spring 容器时，则只能通过<code>@Bean</code>来实现。</p><p>3）使用方式</p><p><code>@Component</code>（<code>@Controller</code>、<code>@Service</code>、<code>@Repository</code>）通常是通过类路径扫描来侦测及自动装配到 Spring 容器中。<code>@Bean</code>一般结合<code>@Configuration</code>一起使用，也可以配置在类的方法中。</p><h5 id="容器功能注解"><a href="#容器功能注解" class="headerlink" title="容器功能注解"></a>容器功能注解</h5><p>组件添加：</p><p>1、@Configuration</p><p>2、@Bean、@Component、@Controller、@Service</p><p>3、@ComponentScan</p><p>4、@Conditional</p><p>原生配置文件导入：</p><p>@ImportResource</p><p>配置绑定：</p><p>1、@ConfigurationProperties</p><p>2、@EnableConfigurationProperties+@ConfigurationProperties</p><p>3、@Component+@ConfigurationProperties</p><p><code>@Import(&#123;xx.class,xx.class&#125;)</code>，通过类型在容器中创建组件实例，组件名为全类名。</p><p><code>@Conditional</code>，条件装配注解，满足指定条件进行组件注册。参数value为一个class泛型数组。@ConditionalOnBean(name &#x3D; “xxx”)，当ioc容器中存在该对象时，为该组件注册实例。</p><p><code>@ImportResource(&quot;classpath:beans.xml&quot;)</code>，导入spring原生配置文件，支持xml配置。</p><p><code>@EnableConfigurationProperties(xx.class)</code>，为xx.class开启配置属性绑定。</p><p><code>@AutoConfigurationPackage</code>，自动配置包原则，利用Register批量地将主程序所在包下的所有组件批量注册进容器。</p><p><code>@ConfigurationProperties(prefix=&quot;spring&quot;)</code>，为核心配置文件中的属性绑定前缀，在映射的实体类上添加注解，表示将该类注册成bean并配置属性值。</p><h4 id="简化开发"><a href="#简化开发" class="headerlink" title="简化开发"></a>简化开发</h4><p><u>lombok开发</u></p><p>1、在maven项目的pom.xml文件中添加lombok依赖</p><p>2、执行maven导入依赖</p><p>3、添加@Data注解，免去get和set方法</p><p>4、添加@ToString重写toString方法。</p><p><u>spring Initailizr开发</u>，再idea中使用spring Initailizr创建初始化项目，勾选依赖配置和版本，maven自动导入，实现快速初始化项目。</p><p><u>yaml</u>（yaml ain’t markup language）配置，key-value写法，大小写敏感，缩进表层级，#为注释。字符串值用单引号或双引号包围，单引号将\n作为字符串输出，双引号将\n作为换行输出。</p><p>注：标记语言是一种将标记以及文本相关的其他信息结合起来，展现出关于文档结构和数据处理细节的文字编码。</p><p><u>spring-boot-configuration-processor配置处理器</u>，显示配置提示信息，添加依赖：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-configuration-processor<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">optional</span>&gt;</span>true<span class="tag">&lt;/<span class="name">optional</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">build</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">plugins</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">plugin</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-maven-plugin<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">configuration</span>&gt;</span></span><br><span class="line">                <span class="comment">&lt;!--在打包项目的时候排除以下简化开发的插件--&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">excludes</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">exclude</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-configuration-processor<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;/<span class="name">exclude</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">excludes</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">configuration</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">plugin</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">plugins</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">build</span>&gt;</span></span><br></pre></td></tr></table></figure><h4 id="web场景开发"><a href="#web场景开发" class="headerlink" title="web场景开发"></a>web场景开发</h4><p>src&#x2F;main&#x2F;java；src&#x2F;main&#x2F;resources都是classes类路径的根路径，下面的所有文件都在该类路径下。当请求来时，java先动态处理，无法处理后寻找静态资源。默认的静态资源路径有[&#x2F;static，&#x2F;resources，&#x2F;META-INF，&#x2F;public]</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String[] CLASSPATH_RESOURCE_LOCATIONS = &#123; <span class="string">&quot;classpath:/META-INF/resources/&quot;</span>,<span class="string">&quot;classpath:/resources/&quot;</span>, <span class="string">&quot;classpath:/static/&quot;</span>, <span class="string">&quot;classpath:/public/&quot;</span> &#125;;</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line">WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders,</span><br><span class="line">ApplicationContext applicationContext, Optional&lt;Resource&gt; welcomePage, String staticPathPattern) &#123;</span><br><span class="line"><span class="keyword">if</span> (welcomePage.isPresent() &amp;&amp; <span class="string">&quot;/**&quot;</span>.equals(staticPathPattern)) &#123;</span><br><span class="line">        <span class="comment">//要用欢迎页功能，必须是/**</span></span><br><span class="line">logger.info(<span class="string">&quot;Adding welcome page: &quot;</span> + welcomePage.get());</span><br><span class="line">setRootViewName(<span class="string">&quot;forward:index.html&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span> (welcomeTemplateExists(templateAvailabilityProviders, applicationContext)) &#123;</span><br><span class="line">        <span class="comment">// 调用Controller  /index</span></span><br><span class="line">logger.info(<span class="string">&quot;Adding welcome page template: index&quot;</span>);</span><br><span class="line">setRootViewName(<span class="string">&quot;index&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="请求处理"><a href="#请求处理" class="headerlink" title="请求处理"></a>请求处理</h5><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">form</span> <span class="attr">action</span>=<span class="string">&quot;/user&quot;</span> <span class="attr">method</span>=<span class="string">&quot;post&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">name</span>=<span class="string">&quot;_method&quot;</span> <span class="attr">type</span>=<span class="string">&quot;hidden&quot;</span> <span class="attr">value</span>=<span class="string">&quot;delete&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;submit&quot;</span> <span class="attr">value</span>=<span class="string">&quot;delete提交&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br></pre></td></tr></table></figure><p>核心Filter：HiddenHttpMethodFilter；用法： 表单method&#x3D;post，隐藏域 _method&#x3D;put，SpringBoot中手动开启。</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">mvc:</span></span><br><span class="line">    <span class="attr">hiddenmethod:</span></span><br><span class="line">      <span class="attr">filter:</span></span><br><span class="line">        <span class="attr">enabled:</span> <span class="literal">true</span>   <span class="comment">#开启页面表单的Rest功能</span></span><br></pre></td></tr></table></figure><p>设置自定义的methodFilter，编写webconfig配置类，创建filter对象，调用HiddenHttpMethodFilter的setMethodParam方法。</p><p><strong>Rest原理</strong></p><ul><li><p>表单提交会带上<code>_method=PUT</code></p></li><li><p>请求过来被<code>HiddenHttpMethodFilter</code>拦截</p></li><li><p>请求是否正常，并且是POST</p></li><li><p>获取到<code>_method</code>的值。</p><ul><li>兼容以下请求；PUT、DELETE、PATCH</li><li>原生request（post），包装模式requesWrapper重写了getMethod方法，返回的是传入的值。</li><li>过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requesWrapper。</li></ul></li></ul><p>Rest使用客户端工具，PostMan直接发送Put、delete等方式请求，无需Filter。</p><p><u>请求映射原理：</u></p><p>SpringMVC功能分析都从 org.springframework.web.servlet.DispatcherServlet-&gt;doDispatch(）</p><p><code>RequestMappingHandlerMapping</code>：保存了所有<code>@RequestMapping</code>和handler的映射规则。</p><p>所有的请求映射都在HandlerMapping中。</p><ul><li>SpringBoot自动配置欢迎页的 WelcomePageHandlerMapping ，访问 &#x2F;能访问到index.html</li><li>SpringBoot自动配置了默认的RequestMappingHandlerMapping</li><li>请求进来，挨个尝试所有的HandlerMapping看是否有请求信息。如果有就找到这个请求对应的handler，如果没有就是下一个 HandlerMapping。</li></ul><p><strong>基本注解：</strong></p><p>路径变量<code>@PathVariable</code>、获取请求头<code>@RequestHeader</code>、模型属性<code>@ModelAttribute</code>、声明请求参数<code>@RequestParam</code>、获取post请求体<code>@RequestBody</code>、声明请求属性<code>@RequestAttribute</code>、矩阵变量<code>@MatrixVariable</code>、获取cookie值<code>@CookieValue</code>、请求处理映射路径<code>@RequestMapping</code>。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.example.demo;</span><br><span class="line"><span class="keyword">import</span> org.springframework.boot.SpringApplication;</span><br><span class="line"><span class="keyword">import</span> org.springframework.boot.autoconfigure.SpringBootApplication;</span><br><span class="line"><span class="keyword">import</span> org.springframework.web.bind.annotation.GetMapping;</span><br><span class="line"><span class="keyword">import</span> org.springframework.web.bind.annotation.RequestParam;</span><br><span class="line"><span class="keyword">import</span> org.springframework.web.bind.annotation.RestController;</span><br><span class="line"></span><br><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DemoApplication</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">      SpringApplication.run(DemoApplication.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@GetMapping(&quot;/hello&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">hello</span><span class="params">(<span class="meta">@RequestParam(value = &quot;name&quot;, defaultValue = &quot;World&quot;)</span> String name)</span> &#123;</span><br><span class="line">      <span class="keyword">return</span> String.format(<span class="string">&quot;Hello %s!&quot;</span>, name);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>Servlet API：</strong></p><p>WebRequest、ServletRequest、MultipartRequest、 HttpSession、javax.servlet.http.PushBuilder、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId。</p><p><strong>复杂参数</strong>：</p><p><strong>Map</strong>、<strong>Model</strong>（map、model里面的数据会被放在request的请求域  request.setAttribute）、Errors&#x2F;BindingResult、RedirectAttributes（ 重定向携带数据）、<strong>ServletResponse（response）</strong>、SessionStatus、UriComponentsBuilder、ServletUriComponentsBuilder</p><p><strong>自定义对象参数：</strong></p><p>可以自动类型转换与格式化，可以级联封装</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Person</span> &#123; </span><br><span class="line">    <span class="keyword">private</span> String userName;</span><br><span class="line">    <span class="keyword">private</span> Integer age;</span><br><span class="line">    <span class="keyword">private</span> Pet pet; </span><br><span class="line">&#125;</span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Pet</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">private</span> String age;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>参数处理原理</strong></p><ul><li>HandlerMapping中找到能处理请求的Handler（Controller.method()）</li><li>为当前Handler 找一个适配器 HandlerAdapter； RequestMappingHandlerAdapter</li><li>适配器执行目标方法并确定方法参数的每一个值</li></ul><p><strong>参数解析器-HandlerMethodArgumentResolver</strong></p><p>确定将要执行的目标方法的每一个参数的值是什么，SpringMVC目标方法能写多少种参数类型。取决于参数解析器。</p><h5 id="响应请求"><a href="#响应请求" class="headerlink" title="响应请求"></a>响应请求</h5><p> 响应JSON ， jackson+@ResponseBody（返回数据不返回页面视图）</p><p>SpringMVC到底支持以下返回值：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">ModelAndView</span><br><span class="line">Model</span><br><span class="line">View</span><br><span class="line">ResponseEntity </span><br><span class="line">ResponseBodyEmitter</span><br><span class="line">StreamingResponseBody</span><br><span class="line">HttpEntity</span><br><span class="line">HttpHeaders</span><br><span class="line">Callable</span><br><span class="line">DeferredResult</span><br><span class="line">ListenableFuture</span><br><span class="line">CompletionStage</span><br><span class="line">WebAsyncTask</span><br><span class="line">@ResponseBody---&gt; RequestResponseBodyMethodProcessor；</span><br></pre></td></tr></table></figure><p><strong>返回值解析器原理</strong></p><ul><li><p>返回值处理器判断是否支持这种类型返回值 supportsReturnType</p></li><li><p>返回值处理器调用 handleReturnValue 进行处理</p></li><li><p>RequestResponseBodyMethodProcessor 可以处理返回值标了@ResponseBody 注解的</p></li><li><p>底层利用 MessageConverters 进行处理 ，将数据写为json</p></li><li><p>1、内容协商（浏览器默认会以请求头的方式告诉服务器他能接受什么样的内容类型）</p><p>2、服务器最终根据自己自身的能力，决定服务器能生产出什么样内容类型的数据，</p><p>3、SpringMVC会挨个遍历所有容器底层的 HttpMessageConverter ，看谁能处理</p></li><li><p>得到MappingJackson2HttpMessageConverter可以将对象写为json</p></li></ul><p>根据客户端接收能力不同，返回不同媒体类型的数据，只需要改变请求头中Accept字段。Http协议中规定的，告诉服务器本客户端可以接收的数据类型。</p><p><u>导入了jackson处理xml的包</u>，xml的converter就会自动进来:</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"> <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.fasterxml.jackson.dataformat<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>jackson-dataformat-xml<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><p><u>开启浏览器参数方式内容协商功能:</u></p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">    <span class="attr">contentnegotiation:</span></span><br><span class="line">      <span class="attr">favor-parameter:</span> <span class="literal">true</span>  </span><br><span class="line"><span class="comment">#开启基于请求参数内容协商策略</span></span><br></pre></td></tr></table></figure><p><strong>内容协商原理</strong></p><p>1、判断当前响应头中是否已经有确定的媒体类型MediaType。</p><p>2、获取客户端支持接收的内容类型。（获取客户端Accept请求头字段）。</p><p>​    contentNegotiationManager内容协商管理器两种内容协商策略：</p><p>​（1）ParameterContentNegotiationStrategy 基于format参数(mediaType有json;xml)</p><p>​（2）HeaderContentNegotiationStrategy  <u>默认</u>基于请求头(mediaType为xml)</p><p>3、遍历循环所有当前系统的 MessageConverter，看谁支持操作这个对象。</p><p>4、找到支持操作Person的converter，把converter支持的媒体类型统计出来。</p><p>5、客户端需要【application&#x2F;xml】，服务端能力【10种、json、xml】。</p><p>6、进行内容协商的最佳匹配媒体类型。</p><p>7、用支持将对象转为 最佳匹配媒体类型 的converter。调用它进行转化 。</p><p>自定义返回值mediaType数据格式，配置内容协商策略，添加自定义的mediaType类型。</p><p>**自定义MessageConverter **</p><p>实现多协议数据兼容。json、xml…</p><p>(1)、@ResponseBody 返回响应数据出去，调用 RequestResponseBodyMethodProcessor 处理</p><p>(2)、Processor 处理方法返回值，通过 MessageConverter 处理</p><p>(3)、所有 MessageConverter 合起来可以支持各种媒体类型数据的操作（读、写）</p><p>(4)、内容协商找到最终的 messageConverter</p><h4 id="thymeleaf"><a href="#thymeleaf" class="headerlink" title="thymeleaf"></a>thymeleaf</h4><p>一个XML&#x2F;XHTML&#x2F;HTML5模板引擎，可用于Web与非Web环境中的应用开发。它是一个开源的现代化服务端Java模板引擎，是整合 Spring MVC 的可选模块，在应用开发中，使用 Thymeleaf 来代替 JSP或其他模板引擎。<a href="thymeleaf.org">官网</a></p><p>注：由于SpringBoot打包是以jar的方式，不是war。其次我们的tomcat是嵌入式的，所以现在SpringBoot默认不支持jsp。<a href="https://docs.spring.io/spring-boot/docs/2.2.5.RELEASE/reference/htmlsingle/#using-boot-starter">Spring官方文档</a></p><p> <strong>基本语法</strong> ：</p><table><thead><tr><th>表达式名字</th><th>语法</th><th>用途</th></tr></thead><tbody><tr><td>变量取值</td><td>${…}</td><td>获取请求域、session域、对象等值</td></tr><tr><td>选择变量</td><td>*{…}</td><td>获取上下文对象值</td></tr><tr><td>消息</td><td>#{…}</td><td>获取国际化等值</td></tr><tr><td>链接</td><td>@{…}</td><td>生成链接</td></tr><tr><td>片段表达式</td><td>~{…}</td><td>jsp:include 作用，引入公共页面片段</td></tr></tbody></table><p> 2、字面量 </p><p>文本值: ‘one text’ , ‘Another one!’ ,…数字: 0 , 34 , 3.0 , 12.3 ,…布尔值: true , false</p><p>空值: null</p><p>变量： one，two，…. 变量不能有空格</p><p> 3、文本操作 </p><p>字符串拼接: +</p><p>变量替换: |The name is ${name}| </p><p> 4、数学运算 </p><p>运算符: + , - , * , &#x2F; , %</p><p> 5、布尔运算 </p><p>运算符:  and , or</p><p>一元运算: ! , not </p><p> 6、比较运算 </p><p>比较: &gt; , &lt; , &gt;&#x3D; , &lt;&#x3D; ( gt , lt , ge , le )等式: &#x3D;&#x3D; , !&#x3D; ( eq , ne ) </p><p> 7、条件运算 </p><p>If-then: (if) ? (then)</p><p>If-then-else: (if) ? (then) : (else)</p><p>Default: (value) ?: (defaultvalue) </p><p> 8、特殊操作 </p><p>无操作： _</p><p><strong>迭代</strong>：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">tr</span> <span class="attr">th:each</span>=<span class="string">&quot;prod : $&#123;prods&#125;&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">td</span> <span class="attr">th:text</span>=<span class="string">&quot;$&#123;prod.name&#125;&quot;</span>&gt;</span>Onions<span class="tag">&lt;/<span class="name">td</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">td</span> <span class="attr">th:text</span>=<span class="string">&quot;$&#123;prod.price&#125;&quot;</span>&gt;</span>2.41<span class="tag">&lt;/<span class="name">td</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">td</span> <span class="attr">th:text</span>=<span class="string">&quot;$&#123;prod.inStock&#125;? #&#123;true&#125; : #&#123;false&#125;&quot;</span>&gt;</span>yes<span class="tag">&lt;/<span class="name">td</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">tr</span>&gt;</span></span><br></pre></td></tr></table></figure><p><strong>使用thymeleaf</strong></p><p>引入依赖：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-thymeleaf<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><p>在相关页面中定义命名空间：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">&lt;!DOCTYPE <span class="keyword">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span> <span class="attr">lang</span>=<span class="string">&quot;en&quot;</span> <span class="attr">xmlns:th</span>=<span class="string">&quot;http://www.thymeleaf.org&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">&quot;UTF-8&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">title</span>&gt;</span>Title<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">form</span> <span class="attr">class</span>=<span class="string">&quot;layui-form&quot;</span> <span class="attr">lay-filter</span>=<span class="string">&quot;userForm&quot;</span> <span class="attr">id</span>=<span class="string">&quot;userForm&quot;</span>&gt;</span></span><br><span class="line"> <span class="comment">&lt;!--/*@thymesVar id=&quot;myUserName&quot; type=&quot;ch&quot;*/--&gt;</span></span><br><span class="line"> <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;text&quot;</span> <span class="attr">th:text</span>=<span class="string">&quot;$&#123;myUserName&#125;&quot;</span> <span class="attr">name</span>=<span class="string">&quot;userName&quot;</span> <span class="attr">lay-verify</span>=<span class="string">&quot;required&quot;</span> <span class="attr">placeholder</span>=<span class="string">&quot;请输入用户名&quot;</span> <span class="attr">autocomplete</span>=<span class="string">&quot;off&quot;</span> <span class="attr">class</span>=<span class="string">&quot;layui-input&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">&quot;www.xxxx.com&quot;</span> <span class="attr">th:href</span>=<span class="string">&quot;$&#123;link&#125;&quot;</span>&gt;</span>百度<span class="tag">&lt;/<span class="name">a</span>&gt;</span> </span><br><span class="line"><span class="tag">&lt;/<span class="name">form</span>&gt;</span> </span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@RequestMapping(&quot;/editUser&quot;)</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">editUser</span><span class="params">(Model model)</span>&#123;</span><br><span class="line">    <span class="type">User</span> <span class="variable">u</span> <span class="operator">=</span> userService.getUser();</span><br><span class="line">    model.addAttribute(<span class="string">&quot;myUserName&quot;</span>,u.getUserName());</span><br><span class="line">    model.addAttribute(<span class="string">&quot;myNickName&quot;</span>,u.getNickName());</span><br><span class="line">    model.addAttribute(<span class="string">&quot;link&quot;</span>,<span class="string">&quot;www.baidu.com&quot;</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;user&quot;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="视图解析原理流程"><a href="#视图解析原理流程" class="headerlink" title="视图解析原理流程"></a>视图解析原理流程</h5><p>1、目标方法处理的过程中，所有数据都会被放在<code> ModelAndViewContainer</code> 里面。包括数据和视图地址</p><p>2、方法的参数是一个自定义类型对象（从请求参数中确定的），把他重新放在 <code>ModelAndViewContainer</code></p><p>3、任何目标方法执行完成以后都会返回 ModelAndView（数据和视图地址）。</p><p>4、processDispatchResult  处理派发结果（页面改如何响应）</p><ul><li>render(mv, request, response); 进行页面渲染逻辑</li><li>根据方法的String返回值得到 View 对象（定义了页面的渲染逻辑），所有的视图解析器尝试是否能根据当前返回值得到View对象，得到了  redirect:&#x2F;main.html –&gt; Thymeleaf new RedirectView()，<code>ContentNegotiationViewResolver</code> 里面包含了下面所有的视图解析器，内部还是利用下面所有视图解析器得到视图对象。view.render(mv.getModelInternal(), request, response)，视图对象调用自定义的render进行页面渲染工作。</li><li>RedirectView 如何渲染【重定向到一个页面】<ul><li>1、获取目标url地址</li><li>2、response.sendRedirect(encodedURL)</li></ul></li></ul><p>视图解析：</p><p>1）返回值以 forward: 开始： new InternalResourceView(forwardUrl)–&gt;  转发request.getRequestDispatcher(path).forward(request, response); </p><p>2）返回值以 redirect: 开始： new RedirectView() -&gt; render就是重定向 </p><p>3）返回值是普通字符串： new ThymeleafView() </p><h4 id="HandlerInterceptor拦截器"><a href="#HandlerInterceptor拦截器" class="headerlink" title="HandlerInterceptor拦截器"></a>HandlerInterceptor拦截器</h4><p>定义拦截器，实现HandlerInterceptor接口，重写preHandler方法：</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="keyword">class</span> <span class="title class_">LoginInterceptor</span> <span class="keyword">implements</span> <span class="title class_">HandlerInterceptor</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">preHandle</span><span class="params">(HttpServletRequest request, HttpServletResponse response, Object handler)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="comment">//获取session</span></span><br><span class="line">        <span class="type">HttpSession</span> <span class="variable">httpSession</span> <span class="operator">=</span> request.getSession();</span><br><span class="line">        <span class="keyword">if</span>(httpSession.getAttribute(<span class="string">&quot;loginUser&quot;</span>)==<span class="literal">null</span>)&#123;</span><br><span class="line">            response.sendRedirect(<span class="string">&quot;/f/toLogin&quot;</span>);</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>webMvcConfig重写addInterceptor方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WebConfig</span> <span class="keyword">implements</span> <span class="title class_">WebMvcConfigurer</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addInterceptors</span><span class="params">(InterceptorRegistry registry)</span> &#123;</span><br><span class="line">        registry.addInterceptor(<span class="keyword">new</span> <span class="title class_">LoginInterceptor</span>())</span><br><span class="line">                .addPathPatterns(<span class="string">&quot;/**&quot;</span>)</span><br><span class="line">           .excludePathPatterns(<span class="string">&quot;/&quot;</span>,<span class="string">&quot;/login.html&quot;</span>,<span class="string">&quot;/f/toLogin&quot;</span>,</span><br><span class="line">                                <span class="string">&quot;/user/login&quot;</span>,<span class="string">&quot;/img/*&quot;</span>,<span class="string">&quot;/layui/**&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>拦截器执行过程：</p><p><u>preHandler-&gt;目标方法-&gt;postHandler-&gt;afterCompletion</u></p><p><strong>原理</strong>：</p><p>1、根据当前请求，找到HandlerExecutionChain【可以处理请求的handler以及handler的所有拦截器】</p><p>2、先顺序执行 所有拦截器的 preHandle方法</p><ul><li>如果当前拦截器prehandler返回为true。则执行下一个拦截器的preHandle。</li><li>如果当前拦截器返回为false。直接倒序执行所有已经执行了的拦截器的afterCompletion。</li></ul><p>3、如果任何一个拦截器返回false。直接跳出不执行目标方法。</p><p>4、所有拦截器都返回True，执行目标方法。</p><p>5、倒序执行所有拦截器的postHandle方法。</p><p>6、前面的步骤有任何异常都会直接倒序触发 afterCompletion</p><p>7、页面成功渲染完成以后，也会倒序触发 afterCompletion</p><h4 id="文件上传"><a href="#文件上传" class="headerlink" title="文件上传"></a>文件上传</h4><p>文件上传自动配置类-<code>MultipartAutoConfiguration</code>，spring boot自动配置好了 <code>StandardServletMultipartResolver</code> 文件上传解析器。</p><p>原理步骤：</p><p>1、请求进来使用文件上传解析器判断（isMultipart）并封装（resolveMultipart，返回MultipartHttpServletRequest）文件上传请求。</p><p>2、参数解析器来解析请求中的文件内容封装成MultipartFile。</p><p>3、将request中多个文件信息封装为一个Map ，MultiValueMap&lt;String, MultipartFile&gt;  。</p><p>注：可以FileCopyUtils文件复制工具类的copy方法，实现文件流的拷贝。</p><p>核心配置文件中配置spring文件上传大小：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line"><span class="attr">servlet:</span></span><br><span class="line">       <span class="attr">multipart:</span></span><br><span class="line">          <span class="attr">max-file-size:</span> <span class="string">10MB</span></span><br><span class="line">          <span class="attr">max-request-size:</span> <span class="string">100MB</span></span><br></pre></td></tr></table></figure><p>前端页面表单：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--post提交，上传至/upload，contentType=&quot;multipart/form-data&quot;--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">form</span> <span class="attr">method</span>=<span class="string">&quot;post&quot;</span> <span class="attr">action</span>=<span class="string">&quot;/upload&quot;</span> <span class="attr">enctype</span>=<span class="string">&quot;multipart/form-data&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;file&quot;</span> <span class="attr">name</span>=<span class="string">&quot;file&quot;</span>&gt;</span><span class="tag">&lt;<span class="name">br</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;submit&quot;</span> <span class="attr">value</span>=<span class="string">&quot;提交&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * MultipartFile 自动封装上传过来的文件</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@RequestPart</span>从请求中取multipartFile文件</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@PostMapping(&quot;/upload&quot;)</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">upload</span><span class="params">(<span class="meta">@RequestParam(&quot;username&quot;)</span> String username,</span></span><br><span class="line"><span class="params">                     <span class="meta">@RequestPart(&quot;headerImg&quot;)</span> MultipartFile headerImg,</span></span><br><span class="line"><span class="params">                     <span class="meta">@RequestPart(&quot;photos&quot;)</span> MultipartFile[] photos)</span> </span><br><span class="line">    <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;main&quot;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="异常处理"><a href="#异常处理" class="headerlink" title="异常处理"></a>异常处理</h4><h5 id="错误处理默认规则"><a href="#错误处理默认规则" class="headerlink" title="错误处理默认规则"></a>错误处理默认规则</h5><ul><li><p>默认情况下，Spring Boot提供<code>/error</code>处理所有错误的映射</p></li><li><p>对于服务器端，它将生成JSON响应，其中包含错误，HTTP状态和异常消息的详细信息。对于浏览器端，响应一个“ WhitelabelErrorView”，以HTML格式呈现。</p></li><li><p>要对其进行自定义，添加<code>View</code>解析为<code>error</code></p></li><li><p>要完全替换默认行为，可以实现 <code>ErrorController </code>并注册该类型的Bean定义，或添加ErrorAttributes类型的组件以使用现有机制但替换其内容。</p></li><li><p>templates&#x2F;error&#x2F;下的4xx，5xx页面会被自动解析；</p></li></ul><h5 id="定制错误处理逻辑"><a href="#定制错误处理逻辑" class="headerlink" title="定制错误处理逻辑"></a>定制错误处理逻辑</h5><ul><li><p>自定义错误页</p></li><li><p>error&#x2F;404.html   error&#x2F;5xx.html，有精确的错误状态码页面就匹配精确，没有就找 4xx.html；如果都没有就触发白页。</p></li><li><p>@ControllerAdvice+@ExceptionHandler处理全局异常，底层是使用ExceptionHandlerExceptionResolver</p></li><li><p>@ResponseStatus(返回状态码)+自定义异常 ，底层是使用ResponseStatusExceptionResolver，把@ResponseStatus注解的信息封装成ModelAndView并返回，底层调用 response.sendError(statusCode, resolvedReason)。</p></li><li><p>Spring底层的异常，如参数类型转换异常，使用DefaultHandlerExceptionResolver 处理框架底层的异常。</p></li><li><p>response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage())</p></li><li><p>自定义实现 HandlerExceptionResolver接口的异常解析器，可以作为默认的全局异常处理规则。</p></li><li><p>ErrorViewResolver实现自定义处理异常，response.sendError(411,”自定义异常”)，error请求就会转给controller。异常没有任何人能处理，则tomcat底层调用response.sendError，error请求就会转给controller。BasicErrorController要去的页面地址是ErrorViewResolver 。</p></li></ul><h5 id="异常处理自动配置"><a href="#异常处理自动配置" class="headerlink" title="异常处理自动配置"></a>异常处理自动配置</h5><ul><li><p>ErrorMvcAutoConfiguration  自动配置异常处理规则</p></li><li><p>容器中的组件：类型：DefaultErrorAttributes -&gt; id：errorAttributes。</p></li><li><p>public class DefaultErrorAttributes implements ErrorAttributes, HandlerExceptionResolver。</p></li></ul><p>DefaultErrorAttributes：定义错误页面中可以包含哪些数据。</p><ul><li><p>容器中的组件：类型：BasicErrorController –&gt; id：basicErrorController（json+白页 适配响应）。</p></li><li><p>处理默认 &#x2F;error 路径的请求，页面响应 new ModelAndView(“error”, model)。</p><ul><li>容器中有组件 View-&gt;id是error（响应默认错误页）。</li></ul></li><li><p>容器中放组件 BeanNameViewResolver，按照返回的视图名作为组件的id去容器中找View对象。</p></li><li><p>容器中的组件：类型：DefaultErrorViewResolver -&gt; id：conventionErrorViewResolver。</p></li><li><p>如果发生错误，会以HTTP的状态码作为视图页地址（viewName），找到真正的页面。</p><ul><li>viewName：error&#x2F;404、5xx.html。</li></ul></li></ul><p>如果想要返回页面，就会找error视图StaticView类型的defaultErrorView，默认是一个白页。其中定义了类WhitelabelErrorViewConfiguration。</p><h5 id="异常处理步骤流程"><a href="#异常处理步骤流程" class="headerlink" title="异常处理步骤流程"></a>异常处理步骤流程</h5><p>1、执行目标方法，目标方法运行期间有任何异常都会被catch，而且标志当前请求结束，并且用 dispatchException封装 。</p><p>2、进入视图解析页面渲染</p><p><code>mav = processDispatchResult(processedRequest, response, mappedHandler, mav, dispatchException);</code></p><p>3、处理handler发生的异常，处理完成返回ModelAndView（跳转地址和页面数据）。</p><ul><li><p>遍历所有的handlerExceptionResolvers，找到HandlerExceptionResolver处理器异常解析器。</p></li><li><p>默认异常解析器（DefaultErrorAttributes、DefaultHandlerExceptionResolver、ExceptionHandlerExceptionResolver、ResponseHandlerExceptionResolver）</p></li><li><p>DefaultErrorAttributes定义错误信息(<u>exception、status、stack_trace、error、message、path</u>)，把异常信息保存到request域，并且返回null。默认没有任何人能处理异常，所以异常会被抛出</p></li><li><p>如果没有任何人能处理最终底层就会发送 &#x2F;error 请求，会被底层的BasicErrorController处理。解析错误视图，遍历所有的 ErrorViewResolver解析，找到默认的DefaultErrorViewResolver(作用是把响应状态码作为错误页的地址)，模板引擎最终响应这个页面error&#x2F;xxx.html。</p></li></ul><h4 id="Web原生组件注入（Servlet、Filter、Listener）"><a href="#Web原生组件注入（Servlet、Filter、Listener）" class="headerlink" title="Web原生组件注入（Servlet、Filter、Listener）"></a>Web原生组件注入（Servlet、Filter、Listener）</h4><p><strong>使用Servlet API</strong></p><p><code>@ServletComponentScan(basePackages = &quot;com.xxx.xxx&quot;) </code>:指定原生Servlet组件都放在那里</p><p><code>@WebServlet(urlPatterns = &quot;/my&quot;)</code>：效果：直接响应，没有经过Spring的拦截器。</p><p><code>@WebFilter(urlPatterns=&#123;&quot;/css/\*&quot;,&quot;/images/\*&quot;&#125;)</code></p><p><code>@WebListener</code></p><p><u>DispatchServlet 如何注册进来</u></p><ul><li>容器中自动配置了  DispatcherServlet  属性绑定到 WebMvcProperties；对应的配置文件配置项是 spring.mvc。</li><li>通过<code>ServletRegistrationBean&lt;DispatcherServlet&gt;</code>把 DispatcherServlet  配置进来。</li><li>默认映射的是 &#x2F; 路径。</li></ul><p>Tomcat-Servlet；</p><p><u>多个Servlet都能处理到同一层路径，精确优选原则</u></p><p>A： &#x2F;my&#x2F;</p><p>B： &#x2F;my&#x2F;1</p><p><strong>使用RegistrationBean</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ServletRegistrationBean`, `FilterRegistrationBean`, and `ServletListenerRegistrationBean</span><br></pre></td></tr></table></figure><p><strong>嵌入式Servlet容器</strong></p><p>默认支持的webServer有<code>Tomcat</code>, <code>Jetty</code>, or <code>Undertow</code>，<code>ServletWebServerApplicationContext</code> 容器启动寻找ServletWebServerFactory 并引导创建服务器。</p><p>原理</p><p>(1) SpringBoot应用启动发现当前是Web应用。web场景包-导入tomcat，web应用会创建一个web版的ioc容器 <code>ServletWebServerApplicationContext</code> 。<code>ServletWebServerApplicationContext</code> 启动的时候寻找 <code>ServletWebServerFactory</code>（Servlet 的web服务器工厂—&gt; Servlet 的web服务器）。</p><p>(2) SpringBoot底层默认有很多的WebServer工厂，<code>TomcatServletWebServerFactory</code>, <code>JettyServletWebServerFactory</code>, or <code>UndertowServletWebServerFactory</code>。底层直接会有一个自动配置类<code>ServletWebServerFactoryAutoConfiguration</code>，它导入了<code>ServletWebServerFactoryConfiguration</code>。</p><p>(3) <code>ServletWebServerFactoryConfiguration </code>配置类 根据动态判断系统中到底导入了那个Web服务器的包。（默认是web-starter导入tomcat包），容器中就有<code>TomcatServletWebServerFactory</code> ，它创建出Tomcat服务器并启动。TomcatWebServer 的构造器拥有初始化方法initialize—this.tomcat.start()。</p><p>(4) 内嵌服务器就是手动把启动服务器的代码调用，前提是tomcat核心jar包存在。</p><p><strong>定制Servlet容器</strong></p><ul><li><p>实现 <code>WebServerFactoryCustomizer&lt;ConfigurableServletWebServerFactory&gt; </code>接口的xxxCustomizer定制化器，可以改变xxxx的默认规则。</p></li><li><p>把配置文件的值和ServletWebServerFactory 进行绑定。</p></li><li><p>修改核心配置文件，server.xxx。</p></li><li><p>直接自定义 ConfigurableServletWebServerFactory </p></li><li><p>编写一个配置类实现 WebMvcConfigurer 即可定制化web功能+ @Bean给容器中再扩展一些组件。</p></li></ul><h4 id="Druid数据源"><a href="#Druid数据源" class="headerlink" title="Druid数据源"></a>Druid数据源</h4><ul><li>Druid主要解决的问题就是<code>传统数据库无法解决的大数据量查询性能的问题</code>。</li><li>本质就是<code>一个分布式支持实时数据分析的数据存储系统</code>。</li></ul><p>在实际应用中，MyBatis 可以利用 Druid 作为其连接池，这样可以避免频繁地创建和关闭连接。MyBatis 会指定 Druid 作为连接池，并从中获取连接。这样MyBatis 从 Druid 中获得连接的管理工作，专注于数据处理本身。总结来说，Druid 为 MyBatis 提供了一个高效且可靠的连接池服务，使得MyBatis能够在不需要手动管理连接的情况下进行数据库操作。</p><p>引入druid-starter：</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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.alibaba<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>druid-spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.1.17<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><p>系统中所有filter：</p><table><thead><tr><th>别名</th><th>Filter类名</th></tr></thead><tbody><tr><td>default</td><td>com.alibaba.druid.filter.stat.StatFilter</td></tr><tr><td>stat</td><td>com.alibaba.druid.filter.stat.StatFilter</td></tr><tr><td>mergeStat</td><td>com.alibaba.druid.filter.stat.MergeStatFilter</td></tr><tr><td>encoding</td><td>com.alibaba.druid.filter.encoding.EncodingConvertFilter</td></tr><tr><td>log4j</td><td>com.alibaba.druid.filter.logging.Log4jFilter</td></tr><tr><td>log4j2</td><td>com.alibaba.druid.filter.logging.Log4j2Filter</td></tr><tr><td>slf4j</td><td>com.alibaba.druid.filter.logging.Slf4jLogFilter</td></tr><tr><td>commonlogging</td><td>com.alibaba.druid.filter.logging.CommonsLogFilter</td></tr></tbody></table><p>核心配置：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">datasource:</span>   </span><br><span class="line">    <span class="attr">druid:</span></span><br><span class="line">      <span class="attr">url:</span> <span class="string">jdbc:mysql://localhost:3306/db</span></span><br><span class="line">      <span class="attr">username:</span> <span class="string">root</span></span><br><span class="line">      <span class="attr">password:</span> <span class="number">123456</span></span><br><span class="line">      <span class="attr">driver-class-name:</span> <span class="string">com.mysql.jdbc.Driver</span></span><br><span class="line">      <span class="attr">aop-patterns:</span> <span class="string">com.xxx.admin.*</span>  <span class="comment">#监控SpringBean</span></span><br><span class="line">      <span class="attr">filters:</span> <span class="string">stat,wall</span>     <span class="comment"># 底层开启功能，stat（sql监控），wall（防火墙）</span></span><br><span class="line"></span><br><span class="line">      <span class="attr">stat-view-servlet:</span>   <span class="comment"># 配置监控页功能</span></span><br><span class="line">        <span class="attr">enabled:</span> <span class="literal">true</span></span><br><span class="line">        <span class="attr">login-username:</span> <span class="string">admin</span></span><br><span class="line">        <span class="attr">login-password:</span> <span class="number">123456</span></span><br><span class="line">        <span class="attr">resetEnable:</span> <span class="literal">false</span></span><br><span class="line"></span><br><span class="line">      <span class="attr">web-stat-filter:</span>  <span class="comment"># 监控web</span></span><br><span class="line">        <span class="attr">enabled:</span> <span class="literal">true</span></span><br><span class="line">        <span class="attr">urlPattern:</span> <span class="string">/*</span></span><br><span class="line">        <span class="attr">exclusions:</span> <span class="string">&#x27;*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*&#x27;</span></span><br><span class="line"></span><br><span class="line">      <span class="attr">filter:</span></span><br><span class="line">        <span class="attr">stat:</span>    <span class="comment"># 对上面filters里面的stat的详细配置</span></span><br><span class="line">          <span class="attr">slow-sql-millis:</span> <span class="number">1000</span></span><br><span class="line">          <span class="attr">logSlowSql:</span> <span class="literal">true</span></span><br><span class="line">          <span class="attr">enabled:</span> <span class="literal">true</span></span><br><span class="line">        <span class="attr">wall:</span></span><br><span class="line">          <span class="attr">enabled:</span> <span class="literal">true</span></span><br><span class="line">          <span class="attr">config:</span></span><br><span class="line">            <span class="attr">drop-table-allow:</span> <span class="literal">false</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><p><a href="https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter">SpringBoot配置示例</a></p><p><a href="https://github.com/alibaba/druid/wiki/DruidDataSource%E9%85%8D%E7%BD%AE%E5%B1%9E%E6%80%A7%E5%88%97%E8%A1%A8">配置项列表</a></p><h4 id="整合Redis"><a href="#整合Redis" class="headerlink" title="整合Redis"></a>整合Redis</h4><p>Redis 是一个开源的内存中的数据结构存储系统，它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构。</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-data-redis<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><p>核心配置：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">redis:</span></span><br><span class="line">      <span class="attr">host:</span> <span class="string">r-bp1nc7reqesxisgxpipd.redis.rds.aliyuncs.com</span></span><br><span class="line">      <span class="attr">port:</span> <span class="number">6379</span></span><br><span class="line">      <span class="attr">password:</span> <span class="string">lfy:Lfy123456</span></span><br><span class="line">     <span class="comment">#client-type: jedis</span></span><br><span class="line">     <span class="comment"># jedis:</span></span><br><span class="line">       <span class="comment"># pool:</span></span><br><span class="line">        <span class="comment">#  max-active: 10</span></span><br></pre></td></tr></table></figure><p>自动配置：</p><ul><li>RedisAutoConfiguration 自动配置类。RedisProperties 属性类 ，spring.redis.xxx是对redis的配置。</li><li>连接工厂是准备好的。LettuceConnectionConfiguration、JedisConnectionConfiguration。</li><li>自动注入了RedisTemplate&lt;Object, Object&gt; ： xxxTemplate。</li><li>自动注入了StringRedisTemplate；k：v都是String。</li><li>底层只要我们使用 StringRedisTemplate、RedisTemplate就可以操作redis。</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Test</span></span><br><span class="line">  <span class="keyword">void</span> <span class="title function_">testRedis</span><span class="params">()</span>&#123;</span><br><span class="line">      ValueOperations&lt;String, String&gt; operations = redisTemplate.opsForValue();</span><br><span class="line">      operations.set(<span class="string">&quot;hello&quot;</span>,<span class="string">&quot;world&quot;</span>);</span><br><span class="line">      <span class="type">String</span> <span class="variable">hello</span> <span class="operator">=</span> operations.get(<span class="string">&quot;hello&quot;</span>);</span><br><span class="line">      System.out.println(hello);</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><h4 id="单元测试"><a href="#单元测试" class="headerlink" title="单元测试"></a>单元测试</h4><p> Spring Boot 2.2.0 版本开始引入 JUnit 5 作为单元测试默认库，作为最新版本的JUnit框架，JUnit5与之前版本的Junit框架有很大的不同。由三个不同子项目的几个不同模块组成。</p><p><strong>JUnit 5 &#x3D; JUnit Platform + JUnit Jupiter + JUnit Vintage</strong></p><p>1）JUnit Platform: Junit Platform是在JVM上启动测试框架的基础，不仅支持Junit自制的测试引擎，其他测试引擎也都可以接入。</p><p>2）JUnit Jupiter: 提供了JUnit5的新的编程模型，是JUnit5新特性的核心，内部包含了一个测试引擎，用于在Junit Platform上运行。</p><p>3）JUnit Vintage: 提供了兼容JUnit4.x,Junit3.x的测试引擎。</p><p><u>相关变化：</u></p><ul><li>注解在 org.junit.jupiter.api 包中，断言在 org.junit.jupiter.api.Assertions 类中，前置条件在 org.junit.jupiter.api.Assumptions 类中。</li><li>把@Before 和@After 替换成@BeforeEach 和@AfterEach。</li><li>把@BeforeClass 和@AfterClass 替换成@BeforeAll 和@AfterAll。</li><li>把@Ignore 替换成@Disabled。</li><li>把@Category 替换成@Tag。</li><li>把@RunWith、@Rule 和@ClassRule 替换成@ExtendWith。</li></ul><p><strong>注意</strong>：SpringBoot 2.4 以上版本移除了默认对 Vintage 的依赖，不能使用junit4的功能 @Test，JUnit 5’s Vintage Engine 从spring-boot-starter-test中移除，如果需要继续兼容junit4需要自行引入vintage。</p><p>引入依赖：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-test<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">scope</span>&gt;</span>test<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="comment">&lt;!--兼容junit4--&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.junit.vintage<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>junit-vintage-engine<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">scope</span>&gt;</span>test<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">exclusions</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">exclusion</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.hamcrest<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>hamcrest-core<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">exclusion</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">exclusions</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><p>SpringBoot整合Junit以后,编写测试方法：@Test标注（注意需要使用junit5版本的注解），Junit类具有Spring的功能，@Autowired、@Transactional 标注测试方法，测试完成后自动回滚。</p><h5 id="JUnit5常用注解"><a href="#JUnit5常用注解" class="headerlink" title="JUnit5常用注解"></a>JUnit5常用注解</h5><p><a href="https://junit.org/junit5/docs/current/user-guide/#writing-tests-annotations">JUnit5与JUnit4的注解变化</a></p><ul><li><code>@Test</code> :表示方法是测试方法。但是与JUnit4的@Test不同，他的职责非常单一不能声明任何属性，拓展的测试将会由Jupiter提供额外测试</li><li><code>@ParameterizedTest</code> :表示方法是参数化测试</li><li><code>@RepeatedTest</code> :表示方法可重复执行</li><li><code>@DisplayName</code> :为测试类或者测试方法设置展示名称</li><li><code>@BeforeEach</code> :表示在每个单元测试之前执行</li><li><code>@AfterEach</code> :表示在每个单元测试之后执行</li><li><code>@BeforeAll</code> :表示在所有单元测试之前执行</li><li><code>@AfterAll</code> :表示在所有单元测试之后执行</li><li><code>@Tag</code> :表示单元测试类别，类似于JUnit4中的@Categories</li><li><code>@Disabled</code> :表示测试类或测试方法不执行，类似于JUnit4中的@Ignore</li><li><code>@Timeout</code> :表示测试方法运行如果超过了指定时间将会返回错误</li><li><code>@ExtendWith</code> :为测试类或测试方法提供扩展类引用</li></ul><h5 id="断言assertions"><a href="#断言assertions" class="headerlink" title="断言assertions"></a>断言assertions</h5><p>断言（assertions）是测试方法中的核心部分，用来对测试需要满足的条件进行验证。这些断言方法都是 org.junit.jupiter.api.Assertions 的静态方法。JUnit 5 内置的断言可以分成如下几个类别：</p><p><strong>1、简单断言</strong></p><p>用来对单个值进行简单的验证，前面的断言失败，后面的代码不会执行。</p><table><thead><tr><th>方法</th><th>说明</th></tr></thead><tbody><tr><td>assertEquals</td><td>判断两个对象或两个原始类型是否相等</td></tr><tr><td>assertNotEquals</td><td>判断两个对象或两个原始类型是否不相等</td></tr><tr><td>assertSame</td><td>判断两个对象引用是否指向同一个对象</td></tr><tr><td>assertNotSame</td><td>判断两个对象引用是否指向不同的对象</td></tr><tr><td>assertTrue</td><td>判断给定的布尔值是否为 true</td></tr><tr><td>assertFalse</td><td>判断给定的布尔值是否为 false</td></tr><tr><td>assertNull</td><td>判断给定的对象引用是否为 null</td></tr><tr><td>assertNotNull</td><td>判断给定的对象引用是否不为 null</td></tr></tbody></table><p><strong>2、数组断言</strong></p><p>通过 assertArrayEquals 方法来判断两个对象或原始类型的数组是否相等</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="meta">@DisplayName(&quot;array assertion&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">array</span><span class="params">()</span> &#123;</span><br><span class="line"> assertArrayEquals(<span class="keyword">new</span> <span class="title class_">int</span>[]&#123;<span class="number">1</span>, <span class="number">2</span>&#125;, <span class="keyword">new</span> <span class="title class_">int</span>[] &#123;<span class="number">1</span>, <span class="number">2</span>&#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>3、组合断言</strong></p><p>assertAll 方法接受多个 org.junit.jupiter.api.Executable 函数式接口的实例作为要验证的断言，可以通过 lambda 表达式很容易的提供这些断言</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="meta">@DisplayName(&quot;assert all&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">all</span><span class="params">()</span> &#123;</span><br><span class="line"> assertAll(<span class="string">&quot;Math&quot;</span>,</span><br><span class="line">    () -&gt; assertEquals(<span class="number">2</span>, <span class="number">1</span> + <span class="number">1</span>),</span><br><span class="line">    () -&gt; assertTrue(<span class="number">1</span> &gt; <span class="number">0</span>)</span><br><span class="line"> );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>4、异常断言</strong></p><p>在JUnit4时期，想要测试方法的异常情况时，需要用**@Rule<strong>注解的ExpectedException变量还是比较麻烦的。而JUnit5提供了一种新的断言方式</strong>Assertions.assertThrows()** ,配合函数式编程就可以进行使用。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="meta">@DisplayName(&quot;异常测试&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">exceptionTest</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="type">ArithmeticException</span> <span class="variable">exception</span> <span class="operator">=</span> Assertions.assertThrows(</span><br><span class="line">        <span class="comment">//扔出断言异常</span></span><br><span class="line">        ArithmeticException.class, () -&gt; System.out.println(<span class="number">1</span> % <span class="number">0</span>));</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>5、超时断言</strong></p><p>Junit5还提供了Assertions.assertTimeout() 为测试方法设置了超时时间</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="meta">@DisplayName(&quot;超时测试&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">timeoutTest</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="comment">//如果测试方法时间超过1s将会异常</span></span><br><span class="line">    Assertions.assertTimeout(Duration.ofMillis(<span class="number">1000</span>), () -&gt; Thread.sleep(<span class="number">500</span>));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>6、快速失败</strong></p><p>通过 fail 方法直接使得测试失败</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="meta">@DisplayName(&quot;fail&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">shouldFail</span><span class="params">()</span> &#123;</span><br><span class="line"> fail(<span class="string">&quot;This should fail&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="前置条件（assumptions）"><a href="#前置条件（assumptions）" class="headerlink" title="前置条件（assumptions）"></a>前置条件（assumptions）</h5><p>JUnit 5 中的前置条件类似于断言，不同之处在于不满足的断言会使得测试方法失败，而不满足的前置条件只会使得测试方法的执行终止直接跳过。前置条件可以看成是测试方法执行的前提，当该前提不满足时，就没有继续执行的必要。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@DisplayName(&quot;前置条件&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AssumptionsTest</span> &#123;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">environment</span> <span class="operator">=</span> <span class="string">&quot;DEV&quot;</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="meta">@Test</span></span><br><span class="line"> <span class="meta">@DisplayName(&quot;assumeTrue&quot;)</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">simpleAssume</span><span class="params">()</span> &#123;</span><br><span class="line">    assumeTrue(Objects.equals(<span class="built_in">this</span>.environment, <span class="string">&quot;DEV&quot;</span>));</span><br><span class="line">    assumeFalse(() -&gt; Objects.equals(<span class="built_in">this</span>.environment, <span class="string">&quot;PROD&quot;</span>));</span><br><span class="line"> &#125;</span><br><span class="line"> </span><br><span class="line"> <span class="meta">@Test</span></span><br><span class="line"> <span class="meta">@DisplayName(&quot;assumingThat&quot;)</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">assumeThenDo</span><span class="params">()</span> &#123;</span><br><span class="line">    assumingThat(</span><br><span class="line">       Objects.equals(<span class="built_in">this</span>.environment, <span class="string">&quot;DEV&quot;</span>),</span><br><span class="line">       () -&gt; System.out.println(<span class="string">&quot;In DEV&quot;</span>)</span><br><span class="line">    );</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>assumeTrue 和 assumFalse 确保给定的条件为 true 或 false，不满足条件会使得测试执行终止。assumingThat 的参数是表示条件的布尔值和对应的 Executable 接口的实现对象。只有条件满足时，Executable 对象才会被执行；当条件不满足时，测试执行并不会终止。</p><h5 id="嵌套测试"><a href="#嵌套测试" class="headerlink" title="嵌套测试"></a>嵌套测试</h5><p>JUnit 5 可以通过 Java 中的内部类和<code>@Nested </code>注解实现嵌套测试，从而可以更好的把相关的测试方法组织在一起。在内部类中可以使用<code>@BeforeEach</code> 和<code>@AfterEach</code> 注解，而且嵌套的层次没有限制。但是，嵌套内部的<code>@BeforeEach</code> 和<code>@AfterEach</code> 方法不会对外部的<code>@Test</code>单元测试生效。相反，外层的会对内层的单元测试生效。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">xxx</span> &#123;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="meta">@DisplayName(&quot;is empty&quot;)</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">isEmpty</span><span class="params">()</span> &#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Nested</span></span><br><span class="line">    <span class="meta">@DisplayName(&quot;after&quot;)</span></span><br><span class="line">    <span class="keyword">class</span> <span class="title class_">After</span> &#123;&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="参数化测试"><a href="#参数化测试" class="headerlink" title="参数化测试"></a>参数化测试</h5><p>参数化测试是JUnit5很重要的一个新特性，用不同的参数多次运行测试，为我们的单元测试带来许多便利。</p><p><strong>相关注解：</strong></p><p>@ParameterizedTest：代表这是一个参数化测试单元，而不是普通测试单元。</p><p>@ValueSource：为参数化测试指定入参来源，支持八大基础类以及String类型,Class类型，使用不同的参数进行多次单元测试，而不需要每新增一个参数就新增一个单元测试，省去了很多冗余代码。</p><p>@NullSource: 表示为参数化测试提供一个null的入参。</p><p>@EnumSource: 表示为参数化测试提供一个枚举入参。</p><p>@CsvFileSource：表示读取指定CSV文件内容作为参数化测试入参。</p><p>@MethodSource：表示读取指定方法的返回值作为参数化测试入参(注意方法返回需要是一个流)。</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">@ParameterizedTest</span></span><br><span class="line"><span class="meta">@ValueSource(strings = &#123;&quot;x&quot;, &quot;xx&quot;, &quot;xxx&quot;&#125;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">parameterizedTest1</span><span class="params">(String string)</span> &#123;</span><br><span class="line">    System.out.println(string);</span><br><span class="line">    Assertions.assertTrue(StringUtils.isNotBlank(string));</span><br><span class="line">&#125;</span><br><span class="line"><span class="meta">@ParameterizedTest</span></span><br><span class="line"><span class="meta">@MethodSource(&quot;method&quot;)</span>    <span class="comment">//指定方法名</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testWithExplicitLocalMethodSource</span><span class="params">(String name)</span> &#123;</span><br><span class="line">    System.out.println(name);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">static</span> Stream&lt;String&gt; <span class="title function_">method</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> Stream.of(<span class="string">&quot;xxx&quot;</span>, <span class="string">&quot;xx&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>注：当然如果参数化测试仅仅只能做到指定普通的入参，他的强大之处的地方在于可以支持外部的各类入参。如:CSV,YML,JSON 文件甚至方法的返回值也可以作为入参。只需要去实现<code>ArgumentsProvider</code>接口，任何外部文件都可以作为它的入参。</p><h4 id="指标监控Actuator"><a href="#指标监控Actuator" class="headerlink" title="指标监控Actuator"></a>指标监控Actuator</h4><p>未来每一个微服务在云上部署以后，都需要对其进行监控、追踪、审计、控制等。SpringBoot就抽取了Actuator场景，使得我们每个微服务快速引用即可获得生产级别的应用监控、审计等功能。</p><p>引入依赖：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-actuator<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><p>​</p><p>2.x版本与1.x版本不同在于在支持MVC的基础上支持webFlux函数式编程、注解扩展、丰富的安全策略，底层使用MicroMeter。</p><p><u>访问路径：</u>localhost:8080&#x2F;actuator，localhost:8080&#x2F;actuator&#x2F;EndPoint</p><p>相关配置：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#management是actuator的配置</span></span><br><span class="line"><span class="attr">management:</span></span><br><span class="line">  <span class="attr">endpoints:</span></span><br><span class="line">    <span class="attr">enabled-by-default:</span> <span class="literal">true</span> <span class="comment">#暴露所有JMX端点信息</span></span><br><span class="line">    <span class="attr">web:</span></span><br><span class="line">      <span class="attr">exposure:</span></span><br><span class="line">        <span class="attr">include:</span> <span class="string">&#x27;*&#x27;</span>  <span class="comment">#以web方式暴露所有端点</span></span><br></pre></td></tr></table></figure><h5 id="Actuator-Endpoint"><a href="#Actuator-Endpoint" class="headerlink" title="Actuator Endpoint"></a>Actuator Endpoint</h5><p><strong>常用的Endpoints监控端点</strong></p><table><thead><tr><th>ID</th><th>描述</th></tr></thead><tbody><tr><td><code>auditevents</code></td><td>暴露当前应用程序的审核事件信息。需要一个<code>AuditEventRepository组件</code>。</td></tr><tr><td><code>beans</code></td><td>显示应用程序中所有Spring Bean的完整列表。</td></tr><tr><td><code>caches</code></td><td>暴露可用的缓存。</td></tr><tr><td><code>conditions</code></td><td>显示自动配置的所有条件信息，包括匹配或不匹配的原因。</td></tr><tr><td><code>configprops</code></td><td>显示所有配置<code>@ConfigurationProperties</code>。</td></tr><tr><td><code>env</code></td><td>暴露Spring的属性<code>ConfigurableEnvironment</code></td></tr><tr><td><code>flyway</code></td><td>显示已应用的所有Flyway数据库迁移。 需要一个或多个<code>Flyway</code>组件。</td></tr><tr><td><code>health</code></td><td>显示应用程序运行状况信息。</td></tr><tr><td><code>httptrace</code></td><td>显示HTTP跟踪信息（默认情况下，最近100个HTTP请求-响应）。需要一个<code>HttpTraceRepository</code>组件。</td></tr><tr><td><code>info</code></td><td>显示应用程序信息。</td></tr><tr><td><code>integrationgraph</code></td><td>显示Spring <code>integrationgraph</code> 。需要依赖<code>spring-integration-core</code>。</td></tr><tr><td><code>loggers</code></td><td>显示和修改应用程序中日志的配置。</td></tr><tr><td><code>liquibase</code></td><td>显示已应用的所有Liquibase数据库迁移。需要一个或多个<code>Liquibase</code>组件。</td></tr><tr><td><code>metrics</code></td><td>显示当前应用程序的“指标”信息。</td></tr><tr><td><code>mappings</code></td><td>显示所有<code>@RequestMapping</code>路径列表。</td></tr><tr><td><code>scheduledtasks</code></td><td>显示应用程序中的计划任务。</td></tr><tr><td><code>sessions</code></td><td>允许从Spring Session支持的会话存储中检索和删除用户会话。需要使用Spring Session的基于Servlet的Web应用程序。</td></tr><tr><td><code>shutdown</code></td><td>使应用程序正常关闭。默认禁用。</td></tr><tr><td><code>startup</code></td><td>显示由<code>ApplicationStartup</code>收集的启动步骤数据。需要使用<code>SpringApplication</code>进行配置<code>BufferingApplicationStartup</code>。</td></tr><tr><td><code>threaddump</code></td><td>执行线程转储。</td></tr></tbody></table><p>如果应用程序是Web应用程序（Spring MVC，Spring WebFlux或Jersey），则可以使用以下附加端点：</p><table><thead><tr><th>ID</th><th>描述</th></tr></thead><tbody><tr><td><code>heapdump</code></td><td>返回<code>hprof</code>堆转储文件。</td></tr><tr><td><code>jolokia</code></td><td>通过HTTP暴露JMX bean（需要引入Jolokia，不适用于WebFlux）。需要引入依赖<code>jolokia-core</code>。</td></tr><tr><td><code>logfile</code></td><td>返回日志文件的内容（如果已设置<code>logging.file.name</code>或<code>logging.file.path</code>属性）。支持使用HTTP<code>Range</code>标头来检索部分日志文件的内容。</td></tr><tr><td><code>prometheus</code></td><td>以Prometheus服务器可以抓取的格式公开指标。需要依赖<code>micrometer-registry-prometheus</code>。</td></tr></tbody></table><p><u>最常用的Endpoint</u></p><ul><li>Health：监控状况</li><li>Metrics：运行时指标</li><li>Loggers：日志记录</li></ul><p><u>Health Endpoint</u></p><p>健康检查端点，一般用于在云平台，平台会定时的检查应用的健康状况，status为up表示健康，为down不健康，web访问路径：localhost:8080&#x2F;actuator&#x2F;health。</p><p><u>Metrics Endpoint</u></p><p>提供详细的、层级的、空间指标信息，这些信息可以被主动推送或者被动获取方式得到。相关metric访问路径：localhost:8080&#x2F;actuator&#x2F;metrics&#x2F;http.server.requests。</p><h5 id="管理Endpoints"><a href="#管理Endpoints" class="headerlink" title="管理Endpoints"></a>管理Endpoints</h5><p><strong>1、开启与禁用Endpoints</strong></p><ul><li>默认所有的Endpoint除过shutdown都是开启的。</li><li>需要开启或者禁用某个Endpoint，配置模式为<code>management.endpoint.端点名.enabled = true</code></li></ul><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">management:</span></span><br><span class="line">  <span class="attr">endpoint:</span></span><br><span class="line">    <span class="attr">health:</span></span><br><span class="line">      <span class="attr">show-details:</span> <span class="string">always</span></span><br><span class="line">      <span class="attr">enabled:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure><ul><li>或者禁用所有的Endpoint然后手动开启指定的Endpoint</li></ul><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">management:</span></span><br><span class="line">  <span class="attr">endpoints:</span></span><br><span class="line">    <span class="attr">enabled-by-default:</span> <span class="literal">false</span></span><br><span class="line">  <span class="attr">endpoint:</span></span><br><span class="line">    <span class="attr">beans:</span></span><br><span class="line">      <span class="attr">enabled:</span> <span class="literal">true</span></span><br><span class="line">    <span class="attr">health:</span></span><br><span class="line">      <span class="attr">enabled:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure><p><strong>2、暴露Endpoint</strong>s</p><p>支持的暴露方式</p><ul><li>HTTP：<u>默认只暴露health和info</u></li><li>JMX：默认暴露所有Endpoint，cmd使用<code>jconsole</code>本地访问</li><li>除过health和info，剩下的Endpoint都应该进行保护访问。如果引入SpringSecurity，则会默认配置安全访问规则</li></ul><p><strong>JMX</strong> （Java Management Extensions，即Java管理扩展）是一个为应用程序、设备、系统等植入管理功能的框架，提供了一种简单的、标准的监控和管理资源的性能监控方式。</p><h5 id="定制-Endpoint"><a href="#定制-Endpoint" class="headerlink" title="定制 Endpoint"></a>定制 Endpoint</h5><p><strong>1、定制 Health 信息</strong></p><p>监控端点的类名须以HealthIndicator结尾，方式一实现<code>HealthIndicator</code>接口：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyHealthIndicator</span> <span class="keyword">implements</span> <span class="title class_">HealthIndicator</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Health <span class="title function_">health</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">errorCode</span> <span class="operator">=</span> check(); </span><br><span class="line">        <span class="keyword">if</span> (errorCode != <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> Health.down().withDetail(<span class="string">&quot;Error Code&quot;</span>, errorCode).build();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> Health.up().build();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>方式二继承<code>AbstractHealthIndicator</code>抽象类：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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="keyword">class</span> <span class="title class_">MyhealthHealthIndicator</span> <span class="keyword">extends</span> <span class="title class_">AbstractHealthIndicator</span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">doHealthCheck</span><span class="params">(Health.Builder builder)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        Map&lt;String,Object&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">        <span class="keyword">if</span>(<span class="number">1</span> == <span class="number">1</span>)&#123;</span><br><span class="line">       <span class="comment">// builder.up(); 健康</span></span><br><span class="line">            builder.status(Status.UP);</span><br><span class="line">            map.put(<span class="string">&quot;count&quot;</span>,<span class="number">1</span>);</span><br><span class="line">            map.put(<span class="string">&quot;ms&quot;</span>,<span class="number">100</span>);</span><br><span class="line">        &#125;<span class="keyword">else</span> &#123;</span><br><span class="line"><span class="comment">// builder.down();</span></span><br><span class="line">            builder.status(Status.OUT_OF_SERVICE);</span><br><span class="line">            map.put(<span class="string">&quot;err&quot;</span>,<span class="string">&quot;连接超时&quot;</span>);</span><br><span class="line">            map.put(<span class="string">&quot;ms&quot;</span>,<span class="number">3000</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        builder.withDetail(<span class="string">&quot;code&quot;</span>,<span class="number">100</span>)</span><br><span class="line">                .withDetails(map);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>访问：localhost:8080&#x2F;actuator&#x2F;health 会返回的所有health信息，包括Myhealth。</p><p><strong>2、定制info信息</strong></p><p>常用两种方式：</p><p>1、编写核心配置文件</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">info:</span></span><br><span class="line">  <span class="attr">appName:</span> <span class="string">MyAdmin</span></span><br><span class="line">  <span class="attr">version:</span> <span class="number">1.0</span><span class="number">.0</span></span><br><span class="line">  <span class="comment">#使用@@可以获取maven的pom文件值</span></span><br><span class="line">  <span class="attr">mavenProjectName:</span> <span class="string">@project.artifactId@</span>  </span><br><span class="line">  <span class="attr">mavenProjectVersion:</span> <span class="string">@project.version@</span></span><br></pre></td></tr></table></figure><p>2、编写InfoContributor监控点</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="keyword">import</span> java.util.Collections;</span><br><span class="line"><span class="keyword">import</span> org.springframework.boot.actuate.info.Info;</span><br><span class="line"><span class="keyword">import</span> org.springframework.boot.actuate.info.InfoContributor;</span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Component;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ExampleInfoContributor</span> <span class="keyword">implements</span> <span class="title class_">InfoContributor</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">contribute</span><span class="params">(Info.Builder builder)</span> &#123;</span><br><span class="line">        builder.withDetail(<span class="string">&quot;example&quot;</span>,</span><br><span class="line">                Collections.singletonMap(<span class="string">&quot;key&quot;</span>, <span class="string">&quot;value&quot;</span>))</span><br><span class="line">            .withDetail(<span class="string">&quot;hello&quot;</span>,<span class="string">&quot;world&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>访问：localhost:8080&#x2F;actuator&#x2F;info 会输出以上方式返回的所有info信息。</p><p><strong>3、定制Metrics信息</strong></p><p>SpringBoot支持自动适配的Metrics。</p><p>增加定制Metrics：</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="keyword">class</span> <span class="title class_">MyService</span>&#123;</span><br><span class="line">    Counter counter;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">MyService</span><span class="params">(MeterRegistry meterRegistry)</span>&#123;</span><br><span class="line">        <span class="comment">//通过Registry注册metrics指标</span></span><br><span class="line">         counter = meterRegistry.counter(<span class="string">&quot;myservice.hello.count&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">hello</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="comment">//记录hello方法调用次数</span></span><br><span class="line">        counter.increment();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//也可以使用下面的方式</span></span><br><span class="line"><span class="meta">@Bean</span></span><br><span class="line">MeterBinder <span class="title function_">queueSize</span><span class="params">(Queue queue)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> (registry) -&gt; Gauge.builder(<span class="string">&quot;queueSize&quot;</span>, queue::size).register(registry);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>访问：localhost:8080&#x2F;actuator&#x2F;metrics&#x2F;myservice.hello.count，会输出该监控指标信息。</p><p><strong>4、自定义Endpoint</strong></p><p>使用<code>@EndPoint</code>注解，属性id为EndPointName。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="meta">@Endpoint(id = &quot;EPname&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DockerEndpoint</span> &#123;</span><br><span class="line">    <span class="meta">@ReadOperation</span></span><br><span class="line">    <span class="keyword">public</span> Map <span class="title function_">getDockerInfo</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> Collections.singletonMap(<span class="string">&quot;info&quot;</span>,<span class="string">&quot;docker started...&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@WriteOperation</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">stopDocker</span><span class="params">()</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;docker stopped....&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>访问：localhost:8080&#x2F;actuator&#x2F;EPname 获取以上信息。</p><p>开发ReadinessEndpoint来管理程序是否就绪，或者LivenessEndpoint来管理程序是否存活。</p><h5 id="可视化应用监控服务"><a href="#可视化应用监控服务" class="headerlink" title="可视化应用监控服务"></a>可视化应用监控服务</h5><p>引入springboot-admin-server依赖：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><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>de.codecentric<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-admin-starter-server<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>2.3.1<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>de.codecentric<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-admin-starter-client<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>2.3.1<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><p>在主配置类上添加<code>@EnableAdminServer</code>注解开启admin监控，在核心配置文件中配置：</p><figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#admin访问地址</span></span><br><span class="line"><span class="attr">spring.boot.admin.url</span>=<span class="string">http://localhost:8080</span></span><br><span class="line"><span class="comment">#应用以ip注册</span></span><br><span class="line"><span class="attr">spring.boot.admin.instance.prefer-ip</span>=<span class="string">true</span></span><br><span class="line"><span class="comment">#应用名</span></span><br><span class="line"><span class="attr">spring.application.name</span>=<span class="string">hello</span></span><br><span class="line"><span class="attr">management.endpoints.enabled-by-default</span>=<span class="string">true </span></span><br><span class="line"><span class="comment">#暴露所有端点</span></span><br><span class="line"><span class="attr">management.endpoints.web.exposure.include</span>=<span class="string">*</span></span><br><span class="line"><span class="comment">#以web方式暴露所有端点</span></span><br></pre></td></tr></table></figure><h4 id="高级特性与原理解析"><a href="#高级特性与原理解析" class="headerlink" title="高级特性与原理解析"></a>高级特性与原理解析</h4><h5 id="Profile功能"><a href="#Profile功能" class="headerlink" title="Profile功能"></a>Profile功能</h5><p>为了方便多环境适配，springboot简化了profile功能。</p><p>1、application-profile功能</p><ul><li><p>默认配置文件  application.yaml，任何时候都会加载。</p></li><li><p>指定环境配置文件： application-{env}.yaml，env可为test、prod。</p></li><li><p>激活指定环境并指定相关配置参数。</p><p>激活profile环境方式：</p><p>一、配置文件激活：<code>spring.profiles.active=prod</code></p><p>二、命令行激活：<code>java -jar xxx.jar --spring.profiles.active=prod  --person.name=666</code></p></li><li><p>修改配置文件的任意值，<u>命令行优先。</u></p></li><li><p>默认配置与环境配置同时生效。</p></li><li><p>同名配置项，profile配置优先。</p></li></ul><p>2、<code>@Profile</code>条件装配功能</p><p>该注解使类或方法等在指定配置环境下生效。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration(proxyBeanMethods = false)</span></span><br><span class="line"><span class="meta">@Profile(&quot;prod&quot;)</span><span class="comment">//该类在生产环境下生效</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ProductionConfiguration</span> &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>3、profile分组</p><p>批量加载配置文件，互补配置，注意不要冲突。</p><figure class="highlight properties"><table><tr><td class="gutter"><pre><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="attr">spring.profiles.group.myprod[0]</span>=<span class="string">prod</span></span><br><span class="line"><span class="attr">spring.profiles.group.myprod[1]</span>=<span class="string">test</span></span><br><span class="line"><span class="attr">spring.profiles.active</span>=<span class="string">myprod  </span></span><br><span class="line"><span class="comment">#激活</span></span><br></pre></td></tr></table></figure><h5 id="外部化配置"><a href="#外部化配置" class="headerlink" title="外部化配置"></a>外部化配置</h5><p>1、外部配置源</p><p>常用：Java属性文件、YAML文件、环境变量、命令行参数</p><p>2、springboot配置文件查找位置</p><p>(1) classpath 根路径</p><p>(2) classpath 根路径下config目录</p><p>(3) jar包当前目录</p><p>(4) jar包当前目录的config目录</p><p>(5) &#x2F;config子目录的直接子目录</p><p>​</p><p>注：<u>指定环境优先，外部优先，后面的可以覆盖前面的同名配置项</u></p><p>3、配置文件加载顺序</p><ol><li>　当前jar包内部的application.properties和application.yml</li><li>　当前jar包内部的application-{profile}.properties 和 application-{profile}.yml</li><li>　引用的外部jar包的application.properties和application.yml</li><li>　引用的外部jar包的application-{profile}.properties 和 application-{profile}.yml</li></ol><h5 id="springboot启动过程"><a href="#springboot启动过程" class="headerlink" title="springboot启动过程"></a>springboot启动过程</h5><p>starter启动原理：</p><ul><li>autoconfigure包中配置使用 <code>META-INF/spring.factories </code>中 <code>EnableAutoConfiguration</code>的值，使得项目启动加载指定的自动配置类。</li><li>编写自动配置类 xxxAutoConfiguration -&gt; xxxxProperties</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><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">@Configuration</span><br><span class="line">@Conditional</span><br><span class="line">@EnableConfigurationProperties</span><br><span class="line">@Bean</span><br></pre></td></tr></table></figure><p><u>过程</u>：引入starter —&gt;xxxAutoConfiguration —&gt; 容器中放入组件 —&gt; 绑定xxxProperties.java —&gt; 配置项</p><p>springboot应用启动过程：</p><ul><li><p>创建 SpringApplication</p><ul><li>保存一些信息。</li><li>判定当前应用的类型。</li><li><code>bootstrappers</code>：初始启动引导器，去spring.factories文件中找 <code>org.springframework.boot.Bootstrapper</code></li><li>找 ApplicationContextInitializer初始化器·，去spring.factories找<code> ApplicationContextInitializer</code>。</li><li>找 ApplicationListener应用监听器(事件通知)，去spring.factories找<code>ApplicationListener</code>。</li></ul></li><li><p>运行 SpringApplication</p><ul><li>StopWatch监控应用启停。</li><li>记录应用的启动时间。</li><li>创建引导上下文（Context环境）createBootstrapContext()，获取到所有之前的 bootstrappers 挨个执行 intitialize() 来完成对引导启动器上下文环境设置。</li><li>让当前应用进入headless模式。</li><li>获取所有RunListener（运行监听器），<code>getSpringFactoriesInstances</code> 去<code>spring.factories</code>找 <code>SpringApplicationRunListener</code>。</li><li>遍历<code>SpringApplicationRunListener</code>调用 starting 方法，相当于通知所有感兴趣系统正在启动过程的人，项目正在 starting。</li><li>保存命令行参数<code>ApplicationArguments</code></li><li>准备环境 prepareEnvironment（）<ul><li>返回或者创建基础环境信息对象StandardServletEnvironment</li><li>配置环境信息对象，读取所有的配置源的配置属性值</li><li>绑定环境信息</li><li>监听器调用<code>listener.environmentPrepared()</code>通知所有的监听器当前环境准备完成</li><li>创建IOC容器createApplicationContext<ul><li>根据项目类型（Servlet）创建容器</li><li>当前会创建AnnotationConfigServletWebServerApplicationContext</li></ul></li></ul></li><li>准备ApplicationContext IOC容器的基本信息 prepareContext()<ul><li>保存环境信息</li><li>IOC容器的后置处理流程</li><li>应用初始化器applyInitializers<ul><li>遍历所有的 ApplicationContextInitializer，调用 initialize.来对ioc容器进行初始化扩展功能</li><li>遍历所有的 listener 调用 contextPrepared，EventPublishRunListenr，通知所有的监听器contextPrepared</li></ul></li><li>所有的监听器 调用 contextLoaded，通知所有的监听器 contextLoaded。</li></ul></li><li><u>刷新IOC容器refreshContext，创建容器中的所有组件</u></li><li>容器刷新完成后工作afterRefresh</li><li>所有监听器调用 listeners.started(context)，通知所有的监听器started</li><li>调用所有runners，callRunners()<ul><li>获取容器中的<code>ApplicationRunner </code></li><li>获取容器中的 <code>CommandLineRunner</code></li><li>合并所有runner并且按照@Order进行排序</li><li>遍历所有的runner，调用 run 方法</li></ul></li><li>如果以上有异常，调用Listener 的 failed方法。</li><li>调用所有监听器的 running 方法listeners.running(context)，通知所有的监听器应用运行中 </li><li>running如果有问题，继续通知 failed ，调用所有 Listener 的 failed，通知所有的监听器 failed</li></ul></li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;springboot是一种简化springweb开发的框架，类似springmvc，他提供各种默认配置，达到开箱即用、敏捷开发的效果。本文主要介绍springboot的依赖管理、自动配置、web开发、thymeleaf与视图解析、拦截器、文件上传、异常处理、web原生组件注入、整合数据源Druid与Redis、Junit单元测试、spring Acutuator性能监控、高级特性与springboot启动原理。</summary>
    
    
    
    <category term="java" scheme="https://goodzou.github.io/categories/java/"/>
    
    
  </entry>
  
  <entry>
    <title>Spring入门</title>
    <link href="https://goodzou.github.io/2023/1231/"/>
    <id>https://goodzou.github.io/2023/1231/</id>
    <published>2023-12-31T10:16:09.000Z</published>
    <updated>2026-02-11T15:25:02.567Z</updated>
    
    <content type="html"><![CDATA[<p>Spring是一个开放源代码的JavaEE设计层面框架，解决了业务逻辑层和其他各层的松耦合问题，因此它将面向接口的编程思想贯穿整个系统应用，它的核心是IOC控制反转和AOP面向切面编程<span id="more"></span>。</p><p>七大模块：core、aop、orm、dao、web、context、web mvc。</p><h4 id="IOC"><a href="#IOC" class="headerlink" title="IOC"></a>IOC</h4><p>控制反转，面向对象的设计原则，用来降低代码之间的耦合度。它有一种Bean对象容器，帮我们创建、管理、装配对象，即bean管理。</p><p><strong>原理</strong>：xml解析、工厂模式、反射</p><p>提供IOC容器的接口：BeanFactory、ApplicationContext(加载配置文件时创建对象)。</p><p>ApplicationContext接口实现类：FileSystemXmlApplicationContext、ClassPathXmlApplicationContext。</p><p>实现IOC的方法：依赖注入。</p><p>bean管理：创建对象、注入属性(装配)。</p><p>bean管理的两种方式：xml配置、注解。</p><p>两种Bean：普通bean、工厂bean。</p><p>bean的作用域：singleton单例（默认）、prototype原型、request、session、application。</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;user&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.xx.xxx.user&quot;</span> <span class="attr">scope</span>=<span class="string">&quot;propotype&quot;</span>&gt;</span></span><br><span class="line"><span class="comment">&lt;!--原型，调用getBean方法时创建不同的对象--&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br></pre></td></tr></table></figure><p>Bean的生命周期：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">1.执行无参构造创建bean</span><br><span class="line">2.调用set方法设置属性值</span><br><span class="line">3.执行初始化方法(postProcess)</span><br><span class="line">4.获取bean实例</span><br><span class="line">5.调用销毁方法销毁</span><br></pre></td></tr></table></figure><h5 id="xml配置创建对象"><a href="#xml配置创建对象" class="headerlink" title="xml配置创建对象"></a>xml配置创建对象</h5><p>配置文件中配置bean。</p><p><code>&lt;bean id=&quot;user&quot; class=&quot;com.xx.xxx.user&quot; name=&quot;别名&quot;&gt;</code></p><p>id：唯一标识</p><p>class：全类名</p><p>默认通过无参构造创建对象。</p><p>有参构造创建：1）通过对象构造器参数下标 2）通过参数名 3）通过参数类型</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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;user&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.xx.xxx.user&quot;</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">constructor-arg</span> <span class="attr">index</span>=<span class="string">&quot;0&quot;</span> <span class="attr">value</span>=<span class="string">&quot;xx&quot;</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">constructor-arg</span> <span class="attr">name</span>=<span class="string">&quot;xx&quot;</span> <span class="attr">value</span>=<span class="string">&quot;xx&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">constructor-arg</span> <span class="attr">type</span>=<span class="string">&quot;int&quot;</span> <span class="attr">value</span>=<span class="string">&quot;xx&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br></pre></td></tr></table></figure><h5 id="xml配置注入属性"><a href="#xml配置注入属性" class="headerlink" title="xml配置注入属性"></a>xml配置注入属性</h5><p>1）set方法。</p><p>2）配置文件中配置属性注入。</p><p><strong>属性注入方式</strong>：property标签 、collection集合、p命名空间 、c命名空间。</p><p>（1）property配置：</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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;user&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.xx.xxx.user&quot;</span> <span class="attr">name</span>=<span class="string">&quot;别名&quot;</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">property</span> <span class="attr">name</span>=<span class="string">&quot;xxx&quot;</span> <span class="attr">value</span>=<span class="string">&quot;xxx&quot;</span>/&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--null注入--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;xxx&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">null</span>/&gt;</span>   </span><br><span class="line">    <span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--特殊符号注入--&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--转义或CDATA--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;xxx&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">value</span>&gt;</span>&lt;![CDATA[&lt;&lt;年龄&gt;&gt;]]&gt;<span class="tag">&lt;/<span class="name">value</span>&gt;</span>   </span><br><span class="line">    <span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!--外部Bean注入，ref指向哪个bean--&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--类中需定义对象属性，set方法--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;xxx&quot;</span> <span class="attr">ref</span>=<span class="string">&quot;xxx&quot;</span>/&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--内部Bean注入(对象注入)--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;xxx&quot;</span>&gt;</span></span><br><span class="line"> <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;dept&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.xx.xx.Dept&quot;</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">&quot;dname&quot;</span> <span class="attr">value</span>=<span class="string">&quot;xx&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line"><span class="comment">&lt;!--通过级联赋值，对多个对象属性赋值，需要设置对象属性的get方法--&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--&lt;property name=&quot;dept.dname&quot; value=&quot;xx&quot;/&gt;--&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!--数组注入--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;xxx&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">array</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">value</span>&gt;</span>1<span class="tag">&lt;/<span class="name">value</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">value</span>&gt;</span>2<span class="tag">&lt;/<span class="name">value</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">array</span>&gt;</span>    </span><br><span class="line">    <span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">   <span class="comment">&lt;!--map注入--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;xxx&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">map</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">entry</span> <span class="attr">key</span>=<span class="string">&quot;x&quot;</span> <span class="attr">value</span>=<span class="string">&quot;x&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">entry</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">map</span>&gt;</span>    </span><br><span class="line">    <span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--list类型注入--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;xxx&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">list</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">value</span>&gt;</span>1<span class="tag">&lt;/<span class="name">value</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">value</span>&gt;</span>2<span class="tag">&lt;/<span class="name">value</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!--list中存对象&lt;ref bean=&quot;外部bean对象&quot;&gt;&lt;/ref&gt;--&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">list</span>&gt;</span>    </span><br><span class="line">    <span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br></pre></td></tr></table></figure><p>测试获取bean对象：</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="type">ApplicationContext</span>  <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassPathXmlApplicationContext</span>(<span class="string">&quot;beans.xml&quot;</span>);</span><br><span class="line"><span class="type">xxx</span> <span class="variable">x</span> <span class="operator">=</span> (xxx)context.getBean(<span class="string">&quot;xxx&quot;</span>);</span><br></pre></td></tr></table></figure><p>（2）p命名空间注入</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&quot;</span></span></span><br><span class="line"><span class="tag"><span class="attr">xmlns:xsi</span>=<span class="string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span></span></span><br><span class="line"><span class="tag"><span class="attr">xmlns:p</span>=<span class="string">&quot;http://www.springframework.org/schema/p&quot;</span></span></span><br><span class="line"><span class="tag"><span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://www.springframework.org/schema/beans</span></span></span><br><span class="line"><span class="string"><span class="tag">https://www.springframework.org/schema/beans/spring-beans.xsd&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;user&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.xx.xx.user&quot;</span> <span class="attr">p:name</span>=<span class="string">&quot;xx&quot;</span> <span class="attr">p:age</span>=<span class="string">&quot;12&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure><p>（3）c命名空间注入</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&quot;</span></span></span><br><span class="line"><span class="tag"><span class="attr">xmlns:xsi</span>=<span class="string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span></span></span><br><span class="line"><span class="tag"><span class="attr">xmlns:c</span>=<span class="string">&quot;http://www.springframework.org/schema/c&quot;</span></span></span><br><span class="line"><span class="tag"><span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://www.springframework.org/schema/beans</span></span></span><br><span class="line"><span class="string"><span class="tag">https://www.springframework.org/schema/beans/spring-beans.xsd&quot;</span>&gt;</span></span><br><span class="line"><span class="comment">&lt;!--constructor构造器参数注入--&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">bean</span> <span class="attr">id</span>=<span class="string">&quot;user&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.xx.xx.user&quot;</span> <span class="attr">c:name</span>=<span class="string">&quot;xx&quot;</span> <span class="attr">c:age</span>=<span class="string">&quot;12&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure><p>（4）公共集合属性提取</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></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&quot;</span></span></span><br><span class="line"><span class="tag"><span class="attr">xmlns:xsi</span>=<span class="string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span></span></span><br><span class="line"><span class="tag"><span class="attr">xmlns:util</span>=<span class="string">&quot;http://www.springframework.org/schema/util&quot;</span></span></span><br><span class="line"><span class="tag"><span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://www.springframework.org/schema/beans</span></span></span><br><span class="line"><span class="string"><span class="tag">https://www.springframework.org/schema/beans/spring-beans.xsd</span></span></span><br><span class="line"><span class="string"><span class="tag">http://www.springframework.org/schema/util</span></span></span><br><span class="line"><span class="string"><span class="tag">https://www.springframework.org/schema/util/spring-util.xsd&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">util:list</span> <span class="attr">id</span>=<span class="string">&quot;booklist&quot;</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;<span class="name">value</span>&gt;</span>xxx<span class="tag">&lt;/<span class="name">value</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">util:util</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;book&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.xx.xx.book&quot;</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">&quot;list&quot;</span> <span class="attr">ref</span>=<span class="string">&quot;booklist&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure><h5 id="自动装配"><a href="#自动装配" class="headerlink" title="自动装配"></a>自动装配</h5><p>spring根据属性名称和属性类型自动将bean的属性值注入。</p><p>byName：默认，在容器中寻找set方法中对应的Beand的id，id唯一。</p><p>byType：寻找和自己属性类型相同的对象，class唯一。</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;pet&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.xx.xx.pet&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;people&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.xx.xxx.user&quot;</span> <span class="attr">autowire</span>=<span class="string">&quot;byName&quot;</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--&lt;property name=&quot;name&quot; ref=&quot;pet&quot;/&gt;--&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br></pre></td></tr></table></figure><h5 id="引入外部属性文件"><a href="#引入外部属性文件" class="headerlink" title="引入外部属性文件"></a>引入外部属性文件</h5><p>引入数据库连接池配置</p><p>1、引入依赖</p><p>2、配置context命名空间</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></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&quot;</span></span></span><br><span class="line"><span class="tag"><span class="attr">xmlns:xsi</span>=<span class="string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span></span></span><br><span class="line"><span class="tag"><span class="attr">xmlns:context</span>=<span class="string">&quot;http://www.springframework.org/schema/context&quot;</span></span></span><br><span class="line"><span class="tag"><span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://www.springframework.org/schema/beans</span></span></span><br><span class="line"><span class="string"><span class="tag">https://www.springframework.org/schema/beans/spring-beans.xsd</span></span></span><br><span class="line"><span class="string"><span class="tag">http://www.springframework.org/schema/context</span></span></span><br><span class="line"><span class="string"><span class="tag">https://www.springframework.org/schema/context/spring-context.xsd&quot;</span>&gt;</span></span><br></pre></td></tr></table></figure><p>3、引入外部文件jdbc.properties</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">context:property-placeholder</span> <span class="attr">location</span>=<span class="string">&quot;classpath:jdbc.properties&quot;</span>/&gt;</span></span><br></pre></td></tr></table></figure><p>4、配置连接池</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&lt;bean id=&quot;dbs&quot; class=&quot;com.alibaba.druid.pool.DruidDataSource&quot; autowire=&quot;byName&quot;&gt;</span><br><span class="line">    &lt;property name=&quot;driverClassName&quot; value=&quot;$&#123;prop.driverClass&#125;&quot;&gt;&lt;/property&gt;</span><br><span class="line">    &lt;property name=&quot;url&quot; value=&quot;$&#123;prop.url&#125;&quot;&gt;&lt;/property&gt;</span><br><span class="line">    &lt;property name=&quot;username&quot; value=&quot;$&#123;prop.userName&#125;&quot;&gt;&lt;/property&gt;</span><br><span class="line">    &lt;property name=&quot;password&quot; value=&quot;$&#123;prop.password&#125;&quot;&gt;&lt;/property&gt;</span><br><span class="line">&lt;/bean&gt;</span><br></pre></td></tr></table></figure><h5 id="注解创建对象"><a href="#注解创建对象" class="headerlink" title="注解创建对象"></a>注解创建对象</h5><p>bean管理中使用注解创建对象，在要放入spring容器的对象上加上<code>@Component</code>注解，按照mvc架构，它的衍生注解有<code>@Repository</code>、<code>@Service</code>、<code>@Controller</code>，属性值设置使用<code>@Value</code>（相当于property配置）。</p><p>1)引入aop依赖。</p><p>2）添加context命名空间，开启注解扫描。</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&quot;</span></span></span><br><span class="line"><span class="tag"><span class="attr">xmlns:xsi</span>=<span class="string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span></span></span><br><span class="line"><span class="tag"><span class="attr">xmlns:context</span>=<span class="string">&quot;http://www.springframework.org/schema/context&quot;</span></span></span><br><span class="line"><span class="tag"><span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://www.springframework.org/schema/beans</span></span></span><br><span class="line"><span class="string"><span class="tag">https://www.springframework.org/schema/beans/spring-beans.xsd</span></span></span><br><span class="line"><span class="string"><span class="tag">http://www.springframework.org/schema/context</span></span></span><br><span class="line"><span class="string"><span class="tag">https://www.springframework.org/schema/context/spring-context.xsd&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">context:component-scan</span> <span class="attr">base-package</span>=<span class="string">&quot;com.xxx&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure><p>3）在类上添加相关的bean注解，<code>@Component(value=&quot;xx&quot;)</code></p><p>默认beanid为类名，首字母小写。value值设置beanid。</p><p>其他注解：</p><p><code>@Nullable</code>，在方法上表示返回值可以为空；用在方法的参数上表示参数可空；用在属性上表示属性可为空。</p><p><code>@Scope</code> : 设置作用域。@Scope(“singleton”):单例模式。</p><p><code>@Configuration</code> : 代表这是一个配置类，就像我们之前看的beans.xml。配合@Bean使用将实体类注册，本质是一个<code>@Component</code>。</p><p><code>@Bean</code> : 注册一个bean，就相当于我们之前写的一个bean标签;这个方法的名字，就相当于bean标签中的id属性;这个方法的返回值，就相当于bean标签中的class属性。</p><p><code>@Import</code> : @Import(XXX.class)可以引入其他配置类，使其合并为一个总配置类，使用时通过AnnotationConfig上下文来获取总配置类即可。</p><p><code>@Data</code> : Lombok中的注解,放在实体类上会自己创建set和get方法，toString方法等。</p><p>自定义扫描哪些注解:</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">context:component-scan</span> <span class="attr">base-package</span>=<span class="string">&quot;com.xxx&quot;</span> <span class="attr">use-default-filter</span>=<span class="string">&quot;false&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">context:include-filter</span> <span class="attr">type</span>=<span class="string">&quot;annotation&quot;</span> <span class="attr">expression</span>=<span class="string">&quot;org.springframework.stereotype.Controller&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">context</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">context:component-scan</span>&gt;</span></span><br></pre></td></tr></table></figure><h5 id="注解注入属性"><a href="#注解注入属性" class="headerlink" title="注解注入属性"></a>注解注入属性</h5><p>常用注解：Autowired、Qualifier、Resource、Value(普通类型注入)。</p><p>1）添加context命名约束</p><p>2）开启注解配置</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">context:component-scan</span> <span class="attr">base-package</span>=<span class="string">&quot;com.xxx&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">context:annotation-config</span>/&gt;</span></span><br></pre></td></tr></table></figure><p>3）添加<code>@Autowired</code>，根据属性类型实现自动装配</p><p>通过属性名称完成属性装配，使用<code>@Qualifier(value=&quot;xxx&quot;)</code>注解指定bean对象。<code>@Resource</code>注解也可以完成自动装配，通过beanid和class类型寻找匹配，名称注入通过name。<code>@Value</code>注解对基本数据类型进行注入。</p><p><strong>@Autowired与@Resource的不同</strong></p><p>1）来源不同<br><code>@Autowired</code>是Spring定义的注解，而<code>@Resource</code>是JSR-250定义的注解。</p><p>2）参数不同<br><code>@Autowired</code>只包含一个参数：required，表示是否开启自动准入，默认是true。而<code>@Resource</code>包含七个参数，其中最重要的两个参数是：name 和 type。<code>@Autowired</code>如果要使用byName，需要使用<code>@Qualifier</code>一起配合。而<code>@Resource</code>如果指定了name，则用byName自动装配，如果指定了type，则用byType自动装配。</p><p>3）使用不同<br><code>@Autowired</code>能够用在：构造器、方法、参数、成员变量和注解上，而<code>@Resource</code>能用在：类、成员变量和方法上。</p><p>4）装配顺序<br><code>@Autowired</code>默认按byType自动装配，而<code>@Resource</code>默认byName自动装配。</p><p><strong>完全注解开发</strong></p><p>Config配置类代替xml文件实现自动装配，实际使用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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> org.springframework.context.annotation.Bean</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.annotation.Configuration</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.annotation.ComponentScan</span><br></pre></td></tr></table></figure><p>在一个类上加上注解<code>@Configuration</code> 代表这是一个配置类，配置类中加上<code>@ComponentScan</code>注解，扫描要注入的bean对象包，替代xml配置文件。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">ApplicationContext</span>  <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AnnotationConfigApplicationContext</span>(config.class);</span><br></pre></td></tr></table></figure><h4 id="AOP"><a href="#AOP" class="headerlink" title="AOP"></a>AOP</h4><p>面向切面编程，通过预编译方式和动态代理实现程序功能统一维护的一种技术。利用AOP可以对业务逻辑的各个部分进行隔离，从而使得业务逻辑各部分之间的耦合度降低，提高程序的可重用性，同时提高了开发的效率。</p><p> 动态代理分为两大类：<u>基于接口的动态代理</u>，<u>基于类的动态代理</u></p><ul><li>基于接口 : JDK动态代理</li><li>基于类 : cglib</li><li>通过反射实现</li></ul><p><strong>专业术语：</strong></p><p>横切关注点：跨越应用程序多个模块的方法和功能，如日志、安全、缓存、事务。<br>切面aspect：增强的过程，被模块化的特殊对象，它是一个类。<br>通知advice：需要增强的代码逻辑部分。<br>切入点pointcut：实际增强的方法，必须定义。<br>连接点jointpoint：可增强的方法。<br>目标target：被通知的对象。<br>代理proxy：向目标对象通知后创建的对象。</p><p>使用AOP需要导入一个依赖包 :</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --&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.aspectj<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>aspectjweaver<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span>   </span><br><span class="line">  <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.9.4<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><h5 id="方式一-通过JDK接口实现"><a href="#方式一-通过JDK接口实现" class="headerlink" title="方式一 : 通过JDK接口实现"></a>方式一 : 通过JDK接口实现</h5><p>创建UserService接口和实现类UserServiceImpl，创建实现类的代理对象，并实现aop切入实现，注意导入约束。</p><p>通过Proxy的newProxyInstance方法创建代理对象。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">import java.lang.reflect.Proxy;</span><br><span class="line">import java.lang.reflect.Method;</span><br><span class="line">import java.lang.reflect.InvocationHandler;</span><br><span class="line"></span><br><span class="line">public class MyProxy&#123;</span><br><span class="line"> public static void main(String[] args)&#123;</span><br><span class="line"> Class[] interfaces = &#123;UserService.class&#125;;</span><br><span class="line"> UserSerivce us = (UserService)Proxy.newProxyInstance(MyProxy.class.getClassLoader(),interfaces,new InvocationHandler()&#123;</span><br><span class="line"> @Override</span><br><span class="line"> public Object invoke(Object proxy,Method method,</span><br><span class="line"> Object[] args) throws Throwable&#123;</span><br><span class="line"> return null;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;)</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="方式二-通过AspectJ实现AOP"><a href="#方式二-通过AspectJ实现AOP" class="headerlink" title="方式二 : 通过AspectJ实现AOP"></a>方式二 : 通过AspectJ实现AOP</h5><p>AspectJ是一个独立的AOP框架，结合spring实现aop操作。需要引入aop、aspectj、cglib、aopalliance依赖。</p><p>切入点表达式：</p><p>execution([权限修饰符] [返回类型] [全类名] [方法名] ([参数列表]))</p><p>例：<code>expression=&quot;execution(* com.xxx.xx.UserServiceImpl.*(..))</code></p><p><strong>（1）xml方式实现：</strong></p><p>1）创建类及其增强类(代理对象类)</p><p>2）添加aop命名空间</p><p>3）xml中配置切入点</p><p>定义类：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">public class book &#123;   </span><br><span class="line">    public void buy()&#123;     </span><br><span class="line">    System.out.println(&quot;---buy-----&quot;);  </span><br><span class="line">    &#125;  </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>定义增强类：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">public class Proxy &#123;   </span><br><span class="line">    public void before()&#123;     </span><br><span class="line">    System.out.println(&quot;----方法执行前------&quot;);  </span><br><span class="line">    &#125;  </span><br><span class="line">    public void after()&#123;     </span><br><span class="line">    System.out.println(&quot;----方法执行后------&quot;); </span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>4）在Spring配置中增强(切入)</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></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--创建两个类的对象--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;book&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.xx.config.book&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;Proxy&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.xx.config.Proxy&quot;</span>/&gt;</span></span><br><span class="line"><span class="comment">&lt;!--aop的配置--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">aop:config</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">aop:pointcut</span> <span class="attr">id</span>=<span class="string">&quot;P&quot;</span> </span></span><br><span class="line"><span class="tag">  <span class="attr">expression</span>=<span class="string">&quot;execution(* com.xx.config.book.buy(..))&quot;</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">aop:aspect</span> <span class="attr">ref</span>=<span class="string">&quot;Proxy&quot;</span>&gt;</span></span><br><span class="line"> <span class="comment">&lt;!--advice增强方法--&gt;</span></span><br><span class="line"> <span class="tag">&lt;<span class="name">aop:before</span> <span class="attr">pointcut-ref</span>=<span class="string">&quot;p&quot;</span> <span class="attr">method</span>=<span class="string">&quot;before&quot;</span>/&gt;</span>       </span><br><span class="line"><span class="tag">&lt;<span class="name">aop:after</span> <span class="attr">pointcut-ref</span>=<span class="string">&quot;p&quot;</span> <span class="attr">method</span>=<span class="string">&quot;after&quot;</span>/&gt;</span>   </span><br><span class="line"><span class="tag">&lt;/<span class="name">aop:aspect</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">aop:config</span>&gt;</span></span><br></pre></td></tr></table></figure><p><strong>（2）注解方式实现：</strong></p><p><code>@Aspect</code>注解表示该类为为增强类或代理对象类。</p><p>1）创建类及其增强类(代理对象类)</p><p>2）添加aop命名空间</p><p>3）开启注解扫描和生成代理对象</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></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&quot;</span></span></span><br><span class="line"><span class="tag"><span class="attr">xmlns:xsi</span>=<span class="string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span></span></span><br><span class="line"><span class="tag"><span class="attr">xmlns:context</span>=<span class="string">&quot;http://www.springframework.org/schema/context&quot;</span></span></span><br><span class="line"><span class="tag"><span class="attr">xmlns:aop</span>=<span class="string">&quot;http://www.springframework.org/schema/aop&quot;</span></span></span><br><span class="line"><span class="tag"><span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;                   http://www.springframework.org/schema/beans</span></span></span><br><span class="line"><span class="string"><span class="tag">https://www.springframework.org/schema/beans/spring-beans.xsd</span></span></span><br><span class="line"><span class="string"><span class="tag">http://www.springframework.org/schema/context</span></span></span><br><span class="line"><span class="string"><span class="tag">https://www.springframework.org/schema/context/spring-context.xsd</span></span></span><br><span class="line"><span class="string"><span class="tag">http://www.springframework.org/schema/aop</span></span></span><br><span class="line"><span class="string"><span class="tag">https://www.springframework.org/schema/aop/spring-aop.xsd&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">context:component-scan</span> <span class="attr">base-package</span>=<span class="string">&quot;com.xxx&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;annotationProxy&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.xxx.config.AnnotationProxy&quot;</span>/&gt;</span></span><br><span class="line"><span class="comment">&lt;!--开启aspectj代理对象生成--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">aop:aspectj-autoproxy</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure><p>aop:aspectj-autoproxy有一个proxy-target-class属性，默认为false，默认使用jdk动态代理。当为true，表示使用CGLib动态代理。不过即使proxy-target-class设置为false，如果目标类没有声明接口，则spring将自动使用CGLib动态代理。</p><p>注：<code>@EnableAspectJAutoProxy</code>注解可以实现代理对象生成，效果一样。</p><p>4）使用注解创建类及增强类的对象</p><p>使用注解配置不同类型的advice通知（增强逻辑）。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">@Aspect</span><br><span class="line">public class AnnotationProxy&#123;</span><br><span class="line">    @Before(</span><br><span class="line">    &quot;execution(* com.x.service.UserServiceImpl.*(..))&quot;) </span><br><span class="line">    public void before()&#123;     </span><br><span class="line">    System.out.println(&quot;------方法执行前------&quot;);</span><br><span class="line">    &#125;   </span><br><span class="line">    @After(</span><br><span class="line">    &quot;execution(* com.x.service.UserServiceImpl.*(..))&quot;)  </span><br><span class="line">    public void after()&#123;       </span><br><span class="line">    System.out.println(&quot;-----方法执行后-------&quot;); </span><br><span class="line">    &#125;   </span><br><span class="line">    @Around(</span><br><span class="line">    &quot;execution(* com.x.service.UserServiceImpl.*(..))&quot;) </span><br><span class="line">    public void around(ProceedingJoinPoint jp) throws Throwable &#123;   </span><br><span class="line">    System.out.println(&quot;环绕前&quot;);  </span><br><span class="line">   System.out.println(&quot;签名:&quot;+jp.getSignature());   </span><br><span class="line">   //执行目标方法proceed     </span><br><span class="line">    Object proceed = jp.proceed();     </span><br><span class="line">    System.out.println(&quot;环绕后&quot;);    </span><br><span class="line">        System.out.println(proceed); </span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>五种advice通知(增强)类型：</strong></p><p><code>@Before</code>前置通知。</p><p><code>@Around</code>环绕通知</p><p><code>@After</code>最终通知</p><p><code>@AfterRetuning</code>后置通知</p><p><code>@AfterThrowing</code>异常通知</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">环绕之前...</span><br><span class="line">before...</span><br><span class="line">目标方法执行...</span><br><span class="line">环绕之后...</span><br><span class="line">after...</span><br><span class="line">afterReturning...</span><br></pre></td></tr></table></figure><p>提取公共切入点：定义一个方法，添加注解<code>@Pointcut(value=&quot;切入点表达式&quot;)</code>，在advice注解设置value值为方法名()。当有多个增强类对同一切入点增强，使用<code>@Order()</code>注解设置增强类优先级。</p><h4 id="JdbcTemplate"><a href="#JdbcTemplate" class="headerlink" title="JdbcTemplate"></a>JdbcTemplate</h4><p>它是spring对jdbc的封装。</p><p>1）导入mysql、druid、jdbc、tx、orm依赖包。</p><p>2）配置数据库连接池</p><p>在引入外部文件中已经配置过。</p><p>3）配置JdbcTemplate对象</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;jdbcTemplate&quot;</span> <span class="attr">class</span>=<span class="string">&quot;org.springframework.jdbc.core.JdbcTemplate&quot;</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">&quot;dataSource&quot;</span> <span class="attr">ref</span>=<span class="string">&quot;dataSource&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br></pre></td></tr></table></figure><p>4）开启注解扫描，在dao层注入jdbcTemplate对象</p><h4 id="Spring事务管理"><a href="#Spring事务管理" class="headerlink" title="Spring事务管理"></a>Spring事务管理</h4><p>Spring在不同的事务管理API之上定义了一个抽象层，使得开发人员不必了解底层的事务管理器API。Spring的声明事务管理底层原理使用aop。</p><p>Spring支持编程式事务管理和声明式的事务管理。声明式的事务管理有两种实现方式：注解和xml配置。</p><p><strong>注解方式</strong></p><p>1）使用Spring管理事务，导入tx事务依赖，命名空间导入 : tx。</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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&quot;</span></span></span><br><span class="line"><span class="tag"><span class="attr">xmlns:xsi</span>=<span class="string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span></span></span><br><span class="line"><span class="tag"><span class="attr">xmlns:context</span>=<span class="string">&quot;http://www.springframework.org/schema/context&quot;</span></span></span><br><span class="line"><span class="tag"><span class="attr">xmlns:aop</span>=<span class="string">&quot;http://www.springframework.org/schema/aop&quot;</span></span></span><br><span class="line"><span class="tag"><span class="attr">xmlns:tx</span>=<span class="string">&quot;http://www.springframework.org/schema/tx&quot;</span></span></span><br><span class="line"><span class="tag"><span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;  http://www.springframework.org/schema/beans</span></span></span><br><span class="line"><span class="string"><span class="tag">https://www.springframework.org/schema/beans/spring-beans.xsd</span></span></span><br><span class="line"><span class="string"><span class="tag">http://www.springframework.org/schema/context</span></span></span><br><span class="line"><span class="string"><span class="tag">https://www.springframework.org/schema/context/spring-context.xsd</span></span></span><br><span class="line"><span class="string"><span class="tag">http://www.springframework.org/schema/aop</span></span></span><br><span class="line"><span class="string"><span class="tag">https://www.springframework.org/schema/aop/spring-aop.xsd</span></span></span><br><span class="line"><span class="string"><span class="tag">http://www.springframework.org/schema/tx</span></span></span><br><span class="line"><span class="string"><span class="tag">http://www.springframework.org/schema/tx/spring-tx.xsd&quot;</span>&gt;</span></span><br></pre></td></tr></table></figure><p>2）创建事务管理器</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;transactionManager&quot;</span> <span class="attr">class</span>=<span class="string">&quot;org.springframework.jdbc.datasource.DataSourceTransactionManager&quot;</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">&quot;dataSource&quot;</span> <span class="attr">ref</span>=<span class="string">&quot;dataSource&quot;</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br></pre></td></tr></table></figure><p>3）开启事务注解</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">tx:annotation-driven</span> <span class="attr">transaction-manager</span></span></span><br><span class="line"><span class="tag"> = <span class="string">&quot;transactionManager&quot;</span>/&gt;</span>   </span><br></pre></td></tr></table></figure><p>4）在service类上添加事务注解</p><p><code>@Transactional</code>，添加在类上，类中方法开启事务。spring定义了7种事务传播行为。</p><p><strong>xml方式</strong></p><p>1）配置事务管理器</p><p>2）配置事务通知</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--配置事务通知--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">tx:advice</span> <span class="attr">id</span>=<span class="string">&quot;txAdvice&quot;</span> <span class="attr">transaction-manager</span>=<span class="string">&quot;transactionManager&quot;</span>&gt;</span>   </span><br><span class="line"><span class="tag">&lt;<span class="name">tx:attributes</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">tx:method</span> <span class="attr">name</span>=<span class="string">&quot;add&quot;</span> <span class="attr">propagation</span>=<span class="string">&quot;REQUIRED&quot;</span>/&gt;</span>       </span><br><span class="line"><span class="tag">&lt;<span class="name">tx:method</span> <span class="attr">name</span>=<span class="string">&quot;delete&quot;</span> <span class="attr">propagation</span>=<span class="string">&quot;REQUIRED&quot;</span>/&gt;</span>       </span><br><span class="line"><span class="tag">&lt;<span class="name">tx:method</span> <span class="attr">name</span>=<span class="string">&quot;update&quot;</span> <span class="attr">propagation</span>=<span class="string">&quot;REQUIRED&quot;</span>/&gt;</span>       </span><br><span class="line"><span class="tag">&lt;<span class="name">tx:method</span> <span class="attr">name</span>=<span class="string">&quot;search*&quot;</span> <span class="attr">propagation</span>=<span class="string">&quot;REQUIRED&quot;</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">tx:method</span> <span class="attr">name</span>=<span class="string">&quot;get&quot;</span> <span class="attr">read-only</span>=<span class="string">&quot;true&quot;</span>/&gt;</span>  </span><br><span class="line"><span class="tag">&lt;<span class="name">tx:method</span> <span class="attr">name</span>=<span class="string">&quot;*&quot;</span> <span class="attr">propagation</span>=<span class="string">&quot;REQUIRED&quot;</span>/&gt;</span>   <span class="tag">&lt;/<span class="name">tx:attributes</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">tx:advice</span>&gt;</span></span><br></pre></td></tr></table></figure><p>3）配置aop，切入点、切面</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></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--配置aop事务--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">aop:config</span>&gt;</span>   </span><br><span class="line"><span class="tag">&lt;<span class="name">aop:pointcut</span> <span class="attr">id</span>=<span class="string">&quot;txPointcut&quot;</span> </span></span><br><span class="line"><span class="tag">    <span class="attr">expression</span>=<span class="string">&quot;execution(* com.x.dao.*.*(..))&quot;</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">aop:advisor</span> <span class="attr">advice-ref</span>=<span class="string">&quot;txAdvice&quot;</span> <span class="attr">pointcut-ref</span>=<span class="string">&quot;txPointcut&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">aop:config</span>&gt;</span></span><br></pre></td></tr></table></figure><p><strong>完全注解开发</strong></p><p>使用<code>@Configuration</code>、<code>@ComponentScan</code>、<code>@EnableTransactionManager</code>、<code>@Bean</code>注解完成事务。创建数据库连接池，事务管理器，jdbcTemplate。</p><h4 id="整合Mybatis"><a href="#整合Mybatis" class="headerlink" title="整合Mybatis"></a>整合Mybatis</h4><p><strong>编写实体类</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">public class User &#123; </span><br><span class="line">  private int id;  </span><br><span class="line">  private String name;    </span><br><span class="line">  private String pwd; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>编写mybatis-config核心配置文件</strong></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></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span> ?&gt;</span></span><br><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">configuration</span>  <span class="keyword">PUBLIC</span> <span class="string">&quot;-//mybatis.org//DTD Config 3.0//EN&quot;</span>  <span class="string">&quot;http://mybatis.org/dtd/mybatis-3-config.dtd&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">configuration</span>&gt;</span>  </span><br><span class="line"><span class="tag">&lt;<span class="name">typeAliases</span>&gt;</span>      </span><br><span class="line"><span class="tag">&lt;<span class="name">package</span> <span class="attr">name</span>=<span class="string">&quot;com.x.pojo&quot;</span>/&gt;</span> </span><br><span class="line"><span class="tag">&lt;/<span class="name">typeAliases</span>&gt;</span>   </span><br><span class="line"><span class="tag">&lt;<span class="name">environments</span> <span class="attr">default</span>=<span class="string">&quot;development&quot;</span>&gt;</span>    </span><br><span class="line"><span class="tag">&lt;<span class="name">environment</span> <span class="attr">id</span>=<span class="string">&quot;development&quot;</span>&gt;</span>          </span><br><span class="line"><span class="tag">&lt;<span class="name">transactionManager</span> <span class="attr">type</span>=<span class="string">&quot;JDBC&quot;</span>/&gt;</span>      </span><br><span class="line"><span class="tag">&lt;<span class="name">dataSource</span> <span class="attr">type</span>=<span class="string">&quot;POOLED&quot;</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">&quot;driver&quot;</span> <span class="attr">value</span>=<span class="string">&quot;com.mysql.jdbc.Driver&quot;</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">&quot;url&quot;</span> <span class="attr">value</span>=<span class="string">&quot;jdbc:mysql://localhost:3306/mybatis?useSSL=false<span class="symbol">&amp;amp;</span>useUnicode=true<span class="symbol">&amp;amp;</span>characterEncoding=utf8&quot;</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">&quot;username&quot;</span> <span class="attr">value</span>=<span class="string">&quot;root&quot;</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">&quot;password&quot;</span> <span class="attr">value</span>=<span class="string">&quot;123456&quot;</span>/&gt;</span>          </span><br><span class="line"><span class="tag">&lt;/<span class="name">dataSource</span>&gt;</span>      </span><br><span class="line"><span class="tag">&lt;/<span class="name">environment</span>&gt;</span>   </span><br><span class="line"><span class="tag">&lt;/<span class="name">environments</span>&gt;</span>   </span><br><span class="line"><span class="tag">&lt;<span class="name">mappers</span>&gt;</span>       </span><br><span class="line"><span class="tag">&lt;<span class="name">package</span> <span class="attr">name</span>=<span class="string">&quot;com.x.dao&quot;</span>/&gt;</span>   </span><br><span class="line"><span class="tag">&lt;/<span class="name">mappers</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">configuration</span>&gt;</span></span><br></pre></td></tr></table></figure><p>编写UserDao接口</p><figure class="highlight plaintext"><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">public interface UserMapper &#123;  </span><br><span class="line">   public List&lt;User&gt; selectUser();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>编写接口对应的Mapper映射文件</strong></p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span> ?&gt;</span><span class="meta">&lt;!DOCTYPE <span class="keyword">mapper</span>  <span class="keyword">PUBLIC</span> <span class="string">&quot;-//mybatis.org//DTD Mapper 3.0//EN&quot;</span>   <span class="string">&quot;http://mybatis.org/dtd/mybatis-3-mapper.dtd&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">mapper</span> <span class="attr">namespace</span>=<span class="string">&quot;com.xxx.dao.UserMapper&quot;</span>&gt;</span> </span><br><span class="line">    <span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;selectUser&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;User&quot;</span>&gt;</span>   </span><br><span class="line">    select * from user   </span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">mapper</span>&gt;</span></span><br></pre></td></tr></table></figure><p>导入spring-webmvc、spring-jdbc、mybatis、mybatis-spring、mysql-connector-java、aspectjweaver包。</p><p>配置Maven静态资源过滤问题：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">build</span>&gt;</span>   </span><br><span class="line"><span class="tag">&lt;<span class="name">resources</span>&gt;</span>      </span><br><span class="line"><span class="tag">&lt;<span class="name">resource</span>&gt;</span>         </span><br><span class="line"><span class="tag">&lt;<span class="name">directory</span>&gt;</span>src/main/java<span class="tag">&lt;/<span class="name">directory</span>&gt;</span>       </span><br><span class="line"><span class="tag">&lt;<span class="name">includes</span>&gt;</span>            </span><br><span class="line"><span class="tag">&lt;<span class="name">include</span>&gt;</span>**/*.properties<span class="tag">&lt;/<span class="name">include</span>&gt;</span>            </span><br><span class="line"><span class="tag">&lt;<span class="name">include</span>&gt;</span>**/*.xml<span class="tag">&lt;/<span class="name">include</span>&gt;</span>         </span><br><span class="line"><span class="tag">&lt;/<span class="name">includes</span>&gt;</span>        </span><br><span class="line"><span class="tag">&lt;<span class="name">filtering</span>&gt;</span>true<span class="tag">&lt;/<span class="name">filtering</span>&gt;</span>      </span><br><span class="line"><span class="tag">&lt;/<span class="name">resource</span>&gt;</span>  </span><br><span class="line"><span class="tag">&lt;/<span class="name">resources</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">build</span>&gt;</span></span><br></pre></td></tr></table></figure><p><strong>Mybatis-Spring</strong></p><p>MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。它将允许 MyBatis 参与到 Spring 的事务管理之中，创建映射器 mapper 和 SqlSession 并注入到 bean 中，以及将 Mybatis 的异常转换为 Spring 的 DataAccessException。最终，可以做到应用代码不依赖于 MyBatis，Spring 或 MyBatis-Spring。</p><p>在 MyBatis-Spring 中，可使用SqlSessionFactoryBean来创建 SqlSessionFactory。要配置这个工厂 bean，只需要把下面代码放在 Spring 的 XML 配置文件中：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;sqlSessionFactory&quot;</span> <span class="attr">class</span>=<span class="string">&quot;org.mybatis.spring.SqlSessionFactoryBean&quot;</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">&quot;dataSource&quot;</span> <span class="attr">ref</span>=<span class="string">&quot;dataSource&quot;</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br></pre></td></tr></table></figure><h5 id="实现一"><a href="#实现一" class="headerlink" title="实现一"></a>实现一</h5><p>引入Spring配置文件beans.xml</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&quot;</span>   <span class="attr">xmlns:xsi</span>=<span class="string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span>      <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--配置数据源：可以用第三方的，也可用Spring的--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;dataSource&quot;</span> <span class="attr">class</span>=<span class="string">&quot;org.springframework.jdbc.datasource.DriverManagerDataSource&quot;</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">&quot;driverClassName&quot;</span> <span class="attr">value</span>=<span class="string">&quot;com.mysql.jdbc.Driver&quot;</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">&quot;url&quot;</span> <span class="attr">value</span>=<span class="string">&quot;jdbc:mysql://localhost:3306/mybatis?useSSL=true<span class="symbol">&amp;amp;</span>useUnicode=true<span class="symbol">&amp;amp;</span>characterEncoding=utf8&quot;</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">&quot;username&quot;</span> <span class="attr">value</span>=<span class="string">&quot;root&quot;</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">&quot;password&quot;</span> <span class="attr">value</span>=<span class="string">&quot;123456&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br></pre></td></tr></table></figure><p>配置SqlSessionFactory，关联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></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--配置SqlSessionFactory--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;sqlSessionFactory&quot;</span> <span class="attr">class</span>=<span class="string">&quot;org.mybatis.spring.SqlSessionFactoryBean&quot;</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">&quot;dataSource&quot;</span> <span class="attr">ref</span>=<span class="string">&quot;dataSource&quot;</span>/&gt;</span>   </span><br><span class="line"><span class="comment">&lt;!--关联Mybatis--&gt;</span>  </span><br><span class="line"><span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;configLocation&quot;</span> <span class="attr">value</span>=<span class="string">&quot;classpath:mybatis-config.xml&quot;</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">&quot;mapperLocations&quot;</span> <span class="attr">value</span>=<span class="string">&quot;classpath:com/kuang/dao/*.xml&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br></pre></td></tr></table></figure><p>注册sqlSessionTemplate，关联sqlSessionFactory</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></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--注册sqlSessionTemplate , 关联sqlSessionFactory--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;sqlSession&quot;</span> <span class="attr">class</span>=<span class="string">&quot;org.mybatis.spring.SqlSessionTemplate&quot;</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">constructor-arg</span> <span class="attr">index</span>=<span class="string">&quot;0&quot;</span> <span class="attr">ref</span>=<span class="string">&quot;sqlSessionFactory&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br></pre></td></tr></table></figure><p>增加Dao接口的实现类；私有化sqlSessionTemplate</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">public class UserDaoImpl implements UserMapper &#123;  </span><br><span class="line"> //sqlSession不用我们自己创建了，Spring来管理  </span><br><span class="line">private SqlSessionTemplate sqlSession;   </span><br><span class="line">public void setSqlSession(SqlSessionTemplate sqlSession) &#123;       </span><br><span class="line">this.sqlSession = sqlSession; </span><br><span class="line">&#125;   </span><br><span class="line">public List&lt;User&gt; selectUser() &#123;   </span><br><span class="line">UserMapper mapper = sqlSession.getMapper(UserMapper.class);    </span><br><span class="line"> return mapper.selectUser();  </span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>注册bean实现</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;userDao&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.x.dao.UserDaoImpl&quot;</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">&quot;sqlSession&quot;</span> <span class="attr">ref</span>=<span class="string">&quot;sqlSession&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span> ?&gt;</span></span><br><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">configuration</span>  <span class="keyword">PUBLIC</span> <span class="string">&quot;-//mybatis.org//DTD Config 3.0//EN&quot;</span>  <span class="string">&quot;http://mybatis.org/dtd/mybatis-3-config.dtd&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">configuration</span>&gt;</span>   </span><br><span class="line"><span class="tag">&lt;<span class="name">typeAliases</span>&gt;</span>    </span><br><span class="line"><span class="tag">&lt;<span class="name">package</span> <span class="attr">name</span>=<span class="string">&quot;com.x.pojo&quot;</span>/&gt;</span>  </span><br><span class="line"><span class="tag">&lt;/<span class="name">typeAliases</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">configuration</span>&gt;</span></span><br></pre></td></tr></table></figure><p>测试</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">@Test</span><br><span class="line">public void test2()&#123;    </span><br><span class="line">ApplicationContext context = new ClassPathXmlApplicationContext(&quot;beans.xml&quot;);    </span><br><span class="line">  UserMapper mapper = (UserMapper) context.getBean(&quot;userDao&quot;);</span><br><span class="line">  List&lt;User&gt; user = mapper.selectUser();    </span><br><span class="line">  System.out.println(user);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="实现二"><a href="#实现二" class="headerlink" title="实现二"></a>实现二</h5><p>dao继承Support类 , 直接利用 getSqlSession() 获得 , 然后直接注入SqlSessionFactory . 比起方式一 , 不需要管理SqlSessionTemplate , 而且对事务的支持更加友好。</p><p>将UserDaoImpl修改一下</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">public class UserDaoImpl extends SqlSessionDaoSupport implements UserMapper &#123;  </span><br><span class="line">public List&lt;User&gt; selectUser() &#123;   </span><br><span class="line">UserMapper mapper = getSqlSession().getMapper(UserMapper.class);     </span><br><span class="line">return mapper.selectUser(); </span><br><span class="line">&#125;&#125;</span><br></pre></td></tr></table></figure><p>修改bean的配置</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;userDao&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.xx.dao.UserDaoImpl&quot;</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">&quot;sqlSessionFactory&quot;</span> <span class="attr">ref</span>=<span class="string">&quot;sqlSessionFactory&quot;</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br></pre></td></tr></table></figure><h4 id="WebFlux函数式编程"><a href="#WebFlux函数式编程" class="headerlink" title="WebFlux函数式编程"></a>WebFlux函数式编程</h4><p>spring5的新功能，类似springMVC，用于web开发，流行的异步非阻塞的响应式编程框架，基于Reactor的api实现。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;Spring是一个开放源代码的JavaEE设计层面框架，解决了业务逻辑层和其他各层的松耦合问题，因此它将面向接口的编程思想贯穿整个系统应用，它的核心是IOC控制反转和AOP面向切面编程</summary>
    
    
    
    <category term="java" scheme="https://goodzou.github.io/categories/java/"/>
    
    
  </entry>
  
  <entry>
    <title>MySQL入门</title>
    <link href="https://goodzou.github.io/2023/1215/"/>
    <id>https://goodzou.github.io/2023/1215/</id>
    <published>2023-12-15T08:15:23.000Z</published>
    <updated>2026-04-20T16:03:43.666Z</updated>
    
    <content type="html"><![CDATA[<p>本文介绍了什么是sql和nosql以及两者区别；介绍了mysql的特点、函数和SQL语句操作数据库；讲解了mysql事务及ACID原则和事务的4种隔离级别，索引的作用和原理；mysql的权限管理和数据备份恢复；数据库的设计和三大范式；SQL注入问题和避免；JDBC以及DBCP连接池连接数据库。<span id="more"></span></p><h4 id="sql与nosql"><a href="#sql与nosql" class="headerlink" title="sql与nosql"></a>sql与nosql</h4><p>关系型数据库SQL是使用关系模式组织数据的结构化数据库系统。</p><p>非关系型数据库（NoSQL）是一类用于存储和管理非结构化数据的数据库系统。</p><p>关系数据库和非关系数据库区别体现在三个方面：数据存储方式不同；扩展方式不同；对事务性的支持不同。具体可参考：<a href="https://zhuanlan.zhihu.com/p/604736447">区别</a></p><p>关系型数据库的实现：Mysql，SQLserver，Oracle等。</p><p>非关系数据库实现：Redis（键值对），Mongodb（文档型），hbase（列存储），neo4j（Graph图像）。</p><h4 id="mysql"><a href="#mysql" class="headerlink" title="mysql"></a>mysql</h4><p>SQL语言的特点：高度非过程化、面向集合操作、可移植性强、简洁灵活、支持三级模式结构、<a href="https://blog.csdn.net/qq_40036754/article/details/110098896">ACID</a>（原子性，一致性，隔离性，持久性）。</p><p>SQL语言支持三级模式结构，通过外模式对应视图、基本表对应存储文件，以及内模式对应存储文件的方式实现。</p><p>Mysql默认不支持中文，在my.ini中配置编码<code>character-set-server=utf8</code>或，编写sql代码时加上<code>DEFAULT CHARSET=UTF8</code>。Mysql默认localhost本地连接，root为最高权限用户，默认数据库引擎为Innodb（支持事务，行级锁，外键，但是占用空间大）。</p><p><strong>注释</strong>：–，&#x2F;**&#x2F;</p><p>数据库数值类型：tinyint(1byte)、smallint(2byte)、int(4byte)、float(4byte)、double(8byte)、decimal、varchar(0-65535)、text文本串、null</p><p>date：YYYY-MM-DD</p><p>time:  HH:mm:ss</p><p>datetime：YYYY-MMM-DD HH:mm:ss</p><p>timestamp：时间戳，1970到现在的毫秒数</p><p>文档：<a href="https://dev.mysql.com/doc/refman/8.2/en/non-typed-operators.html">reference</a></p><h5 id="DDL定义"><a href="#DDL定义" class="headerlink" title="DDL定义"></a>DDL定义</h5><p>数据定义语言（DDL）：create，drop，alter</p><p>创建数据库：<code>create database 数据库名</code></p><p>切换数据库：USE DATABASE</p><p>修改表：<code>alter table &#39;表名&#39; rename as xxx</code></p><p>增加字段：<code>alter table &#39;表名&#39; add Phone INT(11)</code></p><p>修改字段：<code>alter table &#39;表名&#39; modify Phone VARCHAR(11)</code>；</p><p>修改字段名：<code>alter table &#39;表名&#39; change Phone Phone1 INT(11)</code></p><p>删除字段：<code>alter table &#39;表名&#39; drop Phone INT(11)</code></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="comment">--创建表---</span></span><br><span class="line"><span class="keyword">CREATE TABLE</span> `categoryin`</span><br><span class="line">(</span><br><span class="line">categoryin_id <span class="type">INT</span>(<span class="number">20</span>) <span class="keyword">NOT NULL</span> AUTO_INCREMENT COMMENT <span class="string">&#x27;收入分类id&#x27;</span>,</span><br><span class="line">categoryin_name <span class="type">VARCHAR</span>(<span class="number">20</span>) <span class="keyword">CHARACTER SET</span> utf8 COMMENT <span class="string">&#x27;收入分类名&#x27;</span>,</span><br><span class="line"><span class="keyword">PRIMARY KEY</span> (categoryin_id)</span><br><span class="line">)</span><br><span class="line">ENGINE<span class="operator">=</span>INNODB AUTO_INCREMENT<span class="operator">=</span><span class="number">3</span> <span class="keyword">CHARACTER SET</span> <span class="operator">=</span> utf8 COMMENT<span class="operator">=</span><span class="string">&#x27;收入分类&#x27;</span>;</span><br></pre></td></tr></table></figure><h5 id="DML操作管理"><a href="#DML操作管理" class="headerlink" title="DML操作管理"></a>DML操作管理</h5><p>数据操作语言（DML）：insert , delete , update，truncate</p><p><code>insert into 表名 (字段1,字段2) values(值1，值2);</code></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">INSERT INTO</span> `categoryin` <span class="keyword">VALUES</span> (<span class="number">1</span>,<span class="string">&#x27;工资&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `categoryin` <span class="keyword">VALUES</span> (<span class="number">2</span>,<span class="string">&#x27;彩票&#x27;</span>);</span><br></pre></td></tr></table></figure><p><code>update 表名 set &#39;字段1&#39;=&#39;值1&#39;,&#39;字段2&#39;=&#39;值2&#39; where id=xxx; </code></p><p>where条件运算符：&#x3D;，!&#x3D;(&lt;&gt;)， &gt;，&lt;，between  and，and，or。</p><p><code>delete from 表名 where [条件]</code></p><p>清空表（自增变量归0）：<code>truncate table &#39;表名&#39;</code></p><h5 id="DQL查询"><a href="#DQL查询" class="headerlink" title="DQL查询"></a>DQL查询</h5><p>数据查询语言（DQL）：select</p><p><code>select 字段1,字段2 from 表名 as 别名</code></p><p>字段去重，<code>select distinct &#39;字段&#39; from 表名</code></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="comment">--查询系统版本</span></span><br><span class="line"><span class="keyword">select</span> version();</span><br><span class="line"><span class="comment">--计算</span></span><br><span class="line"><span class="keyword">select</span> <span class="number">2</span><span class="operator">*</span><span class="number">3</span>;</span><br><span class="line"><span class="comment">--查询变量</span></span><br><span class="line"><span class="keyword">select</span> @<span class="variable">@auto_increment_increment</span></span><br><span class="line"><span class="comment">--函数</span></span><br><span class="line"><span class="keyword">select</span> 函数</span><br></pre></td></tr></table></figure><p>模糊查询：</p><p><code>select 字段 from 表名 where xxx like &#39;a__&#39;</code></p><p><code>select 字段 from 表名 where xxx like &#39;%a%&#39;</code></p><p><code>select 字段 from 表名 where xxx in (xxx,xxx,xxx)</code></p><p><code>select 字段 from 表名 where xxx is &#39;xxx&#39;</code></p><p>多表查询：</p><p>联接查询：<code>join 表名 as 别名 on [条件]</code></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span>  mi.in_id, mi.single_in, mi.zhanghu_id,            mi.categoryin_id, mi.date, mi.beizhu,</span><br><span class="line">        z.zhanghu_name,z.zhanghu_money,</span><br><span class="line">        z.create_time,</span><br><span class="line">        cin.categoryin_name</span><br><span class="line">        <span class="keyword">from</span> moneyin <span class="keyword">as</span> mi</span><br><span class="line">        <span class="keyword">join</span> zhanghu <span class="keyword">as</span> z</span><br><span class="line">        <span class="keyword">on</span> mi.zhanghu_id<span class="operator">=</span>z.zhanghu_id</span><br><span class="line">        <span class="keyword">join</span> categoryin cin</span><br><span class="line">        <span class="keyword">on</span> mi.categoryin_id<span class="operator">=</span>cin.categoryin_id</span><br></pre></td></tr></table></figure><p>inner join：只返回两个表中联结字段相等的行。</p><p>left join：返回左表所有值，即使没有匹配(使用null填充)。</p><p>right join：返回右表所有值，即使没有匹配(使用null填充)。</p><p>注意顺序：</p><p>where指定结果满足条件；</p><p>group by按照哪个字段分组 ；</p><p>having过滤分组满足的次要条件；</p><p>order by排序；</p><p>limit分页。</p><p><strong>分页limit和排序order by:</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">--ASC升序--DESC降序</span></span><br><span class="line"><span class="keyword">where</span> ....</span><br><span class="line"><span class="keyword">order</span> <span class="keyword">by</span> xxx <span class="keyword">desc</span></span><br><span class="line">limit <span class="number">0</span>,<span class="number">10</span></span><br><span class="line"><span class="comment">--limit 起始下标,pageSize页大小</span></span><br></pre></td></tr></table></figure><p>子查询：</p><p>在where ..(select …)中嵌套一个查询</p><figure class="highlight sql"><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">select</span> sName</span><br><span class="line"><span class="keyword">from</span> Student</span><br><span class="line"><span class="keyword">where</span> sId <span class="keyword">in</span> (<span class="keyword">select</span> <span class="keyword">distinct</span> sId <span class="keyword">from</span> score <span class="keyword">where</span> tId<span class="operator">=</span>(<span class="keyword">select</span> tId <span class="keyword">from</span> teacher <span class="keyword">where</span> tName<span class="operator">=</span><span class="string">&#x27;Alice&#x27;</span>))</span><br></pre></td></tr></table></figure><h5 id="DCL控制"><a href="#DCL控制" class="headerlink" title="DCL控制"></a>DCL控制</h5><p>数据控制语言（DCL）：grant，revoke 。</p><h5 id="五个约束"><a href="#五个约束" class="headerlink" title="五个约束"></a>五个约束</h5><p>主键约束（唯一约束）（Primay Key Counstraint）：唯一性，非空性。</p><p>唯一约束（Unique Counstraint）：唯一性，可以空，但只能有一个。</p><p>检查约束 (Check Counstraint)</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">alter table</span> <span class="keyword">user</span></span><br><span class="line"><span class="keyword">add constraint</span> CK_Sex <span class="keyword">check</span> (sex<span class="operator">=</span><span class="string">&#x27;男&#x27;</span> <span class="keyword">or</span> sex<span class="operator">=</span><span class="string">&#x27;女&#x27;</span>)</span><br></pre></td></tr></table></figure><p>默认约束 (Default Counstraint)</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">alter table</span> <span class="keyword">user</span></span><br><span class="line"><span class="keyword">add constraint</span> df_age <span class="keyword">default</span> (<span class="string">&#x27;0&#x27;</span>) <span class="keyword">for</span> age</span><br></pre></td></tr></table></figure><p>外键约束 (Foreign Key Counstraint)</p><h4 id="函数"><a href="#函数" class="headerlink" title="函数"></a>函数</h4><p>usage:  <code>select [函数];</code></p><p>常用函数：</p><p>abs绝对值，floor向下取整，celling向上取整，rand产生0-1随机数，char_length字符串长度，concat拼接，sign符号函数，current_date当前日期，now当前时间，localtime本地时间，sysdate系统时间，md加密。</p><p>first：返回指定的字段中第一个记录的值；</p><p>last：返回指定的字段中最后一个记录的值；</p><p>len：返回文本字段中值的长度；</p><p>聚合函数(计算最大、最小、平均值)：</p><p>count计数</p><figure class="highlight sql"><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">select</span> <span class="built_in">count</span>(字段或p<span class="operator">-</span>k) <span class="keyword">from</span> stduent;<span class="comment">--忽略null</span></span><br><span class="line"><span class="keyword">select</span> <span class="built_in">count</span>(<span class="operator">*</span>) <span class="keyword">from</span> student;<span class="comment">--不会忽略null</span></span><br><span class="line"><span class="keyword">select</span> <span class="built_in">count</span>(<span class="number">1</span>) <span class="keyword">from</span> student;<span class="comment">--不忽略null，本质：计算行数</span></span><br></pre></td></tr></table></figure><p>sum求和；avg求平均；max；min</p><p>select sum(‘字段’) as 总和 from 表;<br>select avg(‘字段’) as 平均 from 表;<br>select max(‘字段’) as 最大 from 表;<br>select min(‘字段’) as 最小 from 表;</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="comment">--查询不同课程的平均分&gt;80，最高分，最低分</span></span><br><span class="line"><span class="keyword">select</span> subjectName,<span class="built_in">avg</span>(studentResult) <span class="keyword">as</span> 平均分,<span class="built_in">max</span>(studentResult) <span class="keyword">as</span> 最高分,<span class="built_in">min</span>(studentResult) <span class="keyword">as</span> 最低分 </span><br><span class="line"><span class="keyword">from</span> <span class="keyword">result</span> r</span><br><span class="line"><span class="keyword">inner</span> <span class="keyword">join</span> <span class="string">&#x27;subject&#x27;</span> sub </span><br><span class="line"><span class="keyword">on</span> r.<span class="string">&#x27;subjectNo&#x27;</span><span class="operator">=</span>sub.<span class="string">&#x27;subjectNo&#x27;</span></span><br><span class="line"><span class="comment">--根据不同课程分组</span></span><br><span class="line"><span class="keyword">group</span> <span class="keyword">by</span> r.<span class="string">&#x27;subjectNo&#x27;</span></span><br><span class="line"><span class="keyword">having</span> 平均分<span class="operator">&gt;</span><span class="number">80</span></span><br></pre></td></tr></table></figure><h4 id="事务"><a href="#事务" class="headerlink" title="事务"></a>事务</h4><p>数据库事务(Transaction)：指由一个或多个数据库操作组成的逻辑单位。</p><p>mysql默认开启事务自动提交！</p><p><code>set autocommit=0;</code>设置手动提交事务。</p><p><code>start transaction</code>开启事务。</p><p><code>commit</code>事务提交，事务一旦提交，数据将持久化，回滚也无法恢复。</p><p><code>rollback</code>事务回滚。</p><p><strong>事务原则ACID</strong></p><p><a href="https://blog.csdn.net/qq_40036754/article/details/110098896">ACID</a>（原子性，一致性，隔离性，持久性）。</p><p>原子性atomic：事务要么所有的操作都执行成功,要么所有的操作都不执行,保证数据库的一致性和完整性。</p><p>一致性consistency：事务完成后符合逻辑运算，数据前后状态要一致。</p><p>持久性durability：事务没有提交则数据恢复原状，事务提交则数据不可逆，不会因宕机外部因素丢失数据。</p><p>隔离性isolation：排除其他事务对本次事务的影响。</p><p><strong>事务隔离级别</strong></p><p>脏读：一个事务读取另一个事务未提交的数据。</p><p>不可重复读：一次事务内多次读取某行的数据不同。</p><p>虚读(幻读)：一次事务中读取了其他事务插入的数据。</p><p> 4 种隔离级别：读未提交 (READ UNCOMMITTED)、读已提交 (READ COMMITTED)、可重复读 (REPEATABLE READ)、串行化 (SERIALIZABLE)。</p><table><thead><tr><th>隔离级别</th><th>脏读</th><th>不可重复读</th><th>幻读</th></tr></thead><tbody><tr><td>读未提交</td><td>可能</td><td>可能</td><td>可能</td></tr><tr><td>读提交</td><td>不可能</td><td>可能</td><td>可能</td></tr><tr><td>可重复读</td><td>不可能</td><td>不可能</td><td>可能</td></tr><tr><td>串行化</td><td>不可能</td><td>不可能</td><td>不可能</td></tr></tbody></table><h4 id="索引"><a href="#索引" class="headerlink" title="索引"></a>索引</h4><p>索引定义：：Index是帮助MySQL高效获取数据的数据结构。<br>索引作用：通过索引可以大大的提高数据库的检索速度，提高数据库的性能。</p><h5 id="索引类型"><a href="#索引类型" class="headerlink" title="索引类型"></a>索引类型</h5><p>唯一索引unique：唯一，可重复(多个列都可以标识为unique)</p><p>主键索引primary：唯一，不可重复(一张表只能有一个)。定义表主键的时候，会自动创建主键索引。</p><p>常规索引key：默认</p><p>全文索引FullText：快速定位数据。</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">--1、创建表时添加索引</span></span><br><span class="line"><span class="comment">--2、alter </span></span><br><span class="line"><span class="keyword">alter table</span> 表名 <span class="keyword">add</span> Index_TYPE index <span class="string">&#x27;索引名&#x27;</span>(<span class="string">&#x27;字段&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">--3、create</span></span><br><span class="line"><span class="comment">-- 索引名：id_表名_字段名</span></span><br><span class="line"><span class="keyword">create</span> index 索引名 <span class="keyword">on</span> 表(<span class="string">&#x27;字段&#x27;</span>);</span><br></pre></td></tr></table></figure><p>聚集索引clustered index：表中各行的物理顺序与键值的逻辑顺序相同，每个表只能有一个。在聚集索引中，表中各行的物理顺序与键值的逻辑（索引）顺序相同。表只能包含一个聚集索引。</p><p>非聚集索引：非聚集索引指定表的逻辑顺序，数据存储在一个位置，索引存储在另一个位置，索引中包含指向数据存储位置的指针。</p><p>一个表只能创建1个聚集索引，多个非聚集索引。设置某列为主键，该列就默认为聚集索引。</p><p>索引数据结构：InnoDB使用聚集索引，数据记录本身被存于一棵B+树的叶子节点上，同一个叶子节点内的各条数据记录按主键顺序存放。每当有一条新的记录插入时，MySQL会根据其主键将其插入适当的节点和位置，如果页面达到装载因子，则开辟一个新的页（节点）。</p><h4 id="权限与DCL"><a href="#权限与DCL" class="headerlink" title="权限与DCL"></a>权限与DCL</h4><p>数据库mysql中user表记录了root、mysql.sys、mysql.session等用户的操作权限、访问主机host（默认localhost，只能本地连接）和各自密码(加密)。其中，root用户为最高权限用户，只有root有grant授权权限。</p><p>创建用户：<code>create user xxx identified by &#39;PASSWORD&#39;;</code></p><p>删除用户：<code>drop user xxx;</code></p><p>修改密码：<code>update user set password=&#39;123456&#39; where user=&#39;root&#39;;</code></p><p><code>set password for root = password(&#39;123456&#39;);</code></p><p>修改当前用户密码：<code>set password = password(&#39;123456&#39;);</code></p><p>重命名用户：<code>rename user xxx to xx;</code></p><p><u>数据控制语言（DCL）：grant，revoke 。</u></p><p>授权：<code>grant 某权限[all privileges] on 库.表 to 用户; </code></p><p>查看权限：<code>show grants for xxx</code></p><p>特殊的<code>show grants for root@localhost</code></p><p>撤销权限：<code>revoke 权限[privileges] on 库.表(*.*) from 用户;</code></p><p>当出现1130-host “xxx” is not allowed to connetced to this Mysql错误提示，表示访问拒绝非本地连接，需要修改host为%，可执行以下步骤。</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><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">--1. use mysql;</span></span><br><span class="line"><span class="comment">--2. update user set host=&#x27;%&#x27; where user=&#x27;root&#x27;;</span></span><br><span class="line"><span class="comment">--3. flush privileges;</span></span><br><span class="line"><span class="comment">/*在不重启mysql下使修改生效*/</span></span><br></pre></td></tr></table></figure><h4 id="备份恢复"><a href="#备份恢复" class="headerlink" title="备份恢复"></a>备份恢复</h4><p><strong>备份</strong></p><p>方式一、物理拷贝</p><p>直接将mysql中的data目录下的文件数据拷贝出去。</p><p>方式二、mysqldump命令</p><p><code>mysqldump -h 主机 -u 用户 -p 密码 数据库 表名&gt; 导出位置/xx.sql </code></p><p><code>mysqldump -h 主机 -u 用户 -p 密码 数据库&gt; 导出位置/xx.sql </code></p><p>例：<code>mysqldump -u root -p 123456 mydb &gt; /home/db.bak</code></p><p>方式三、sqlyog可视化导出转储</p><p><strong>恢复</strong></p><p>登录数据库，使用source命令导入备份的sql或bak数据。</p><p><code>source 备份文件</code></p><p>或未登录下</p><p><code>mysqldump -h 主机 -u 用户 -p 密码 数据库&lt; xx.sql </code></p><h4 id="数据库设计"><a href="#数据库设计" class="headerlink" title="数据库设计"></a>数据库设计</h4><p>良好的设计将节省内存，提高查询速度，便于系统开发。</p><p>注意点：表名命名清晰、表结构合理、表关系(实体关系)设计、字段命名规范(不要大小写，统一小写加下划线)、字段类型及长度、主键设计、数据库引擎使用(通常为innodb)、字符集规范utf8。</p><h4 id="三大范式"><a href="#三大范式" class="headerlink" title="三大范式"></a>三大范式</h4><p>数据库三大范式包含：</p><p><strong>第一范式(1NF)</strong>：属性不可分割，每一列都是不可分的原子数据项。</p><p><strong>第二范式(2NF)</strong>：满足第一范式，且不存在部分依赖，每一列必须和主键相关。非码属性必须完全依赖于候选码。</p><p><strong>第三范式(3NF)</strong>：满足第二范式，且不存在传递依赖，每一列必须和主键直接相关，不存在间接相关。任何非主属性不依赖于其它非主属性，即在2NF基础上消除传递依赖。</p><p>设计注意事项：关联性查询最好不要超过三张表，设计时应该考虑性能与规范的问题。</p><h4 id="JDBC"><a href="#JDBC" class="headerlink" title="JDBC"></a>JDBC</h4><p>Java DataBase Connectivity（Java数据库连接），是 JavaEE 平台下的技术规范， 定义了在 Java 语言中连接数据库，执行数据库操作的标准 ，可以为多种关系数据库提供统一访问，其本质是sun公司制定的一套接口。</p><p>因为每一个数据库的底层实现原理都不一样，每一个数据库产品都有自己独特的实现原理！所以为了方便java的开发，制定一个统一的连接数据库的规范势在必行，这就是JDBC。只要数据库厂商面向JDBC编写驱动，就能让开发者使用JDBC方便地操作各种数据库。</p><p>其中，java.sql.*；javax.sql.*里定义了与数据库操作有关的接口和方法。mysql-connector-java-8.0.11.jar包是标准JDBC的驱动程序，用于连接数据库。JdbcUtils工具类封装加载驱动，获取连接，释放资源。</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></pre></td><td class="code"><pre><span class="line"><span class="comment">//加载驱动，执行静态代码块</span></span><br><span class="line">Class.forName(<span class="string">&quot;com.mysql.jdbc.Driver&quot;</span>);</span><br><span class="line"><span class="comment">//url，数据库信息</span></span><br><span class="line">String url=<span class="string">&quot;jdbc:mysql://localhost:3360/数据库?useSSL=true&amp;</span></span><br><span class="line"><span class="string">   characterEncoding=utf-8 &amp; serverTimezone=Asia/Shanghai&quot;</span>;</span><br><span class="line">String user=<span class="string">&quot;root&quot;</span>;</span><br><span class="line">String password=<span class="string">&quot;123456&quot;</span>;</span><br><span class="line"><span class="comment">//数据库连接对象</span></span><br><span class="line"><span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span></span><br><span class="line">DriverManager.getConnection(url,user,password);</span><br><span class="line"><span class="comment">//定义statement对象</span></span><br><span class="line">Statement statement=connection.createStatement();</span><br><span class="line"><span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">&quot;select * from user&quot;</span>;</span><br><span class="line"><span class="comment">//定义结果集</span></span><br><span class="line"><span class="type">ResultSet</span> <span class="variable">resultSet</span> <span class="operator">=</span> statement.executeQuery(sql);</span><br><span class="line"><span class="keyword">while</span>(resultSet.next())&#123;</span><br><span class="line"> System.ou.println(<span class="string">&quot;id=&quot;</span>+resultSet.getObject(<span class="string">&quot;id&quot;</span>));</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//释放连接</span></span><br><span class="line">resultSet.close();</span><br><span class="line">statement.close();</span><br><span class="line">connection.close();</span><br><span class="line"><span class="comment">//jdbcUtils.release(connetcion,statement,resultSet);</span></span><br></pre></td></tr></table></figure><p>oracle数据库连接：<code>jdbc:oracle:thin:@//&lt;host&gt;:&lt;port&gt;/&lt;SERVICE_NAME&gt; </code></p><p>sid连接：<code>jdbc:oracle:thin:@localhost:1521:sid</code></p><p><a href="https://blog.csdn.net/qq_42588990/article/details/111803099">JDBC参考资料</a></p><h4 id="SQL注入问题"><a href="#SQL注入问题" class="headerlink" title="SQL注入问题"></a>SQL注入问题</h4><p>指攻击者在查询信息的结尾上添加额外的SQL语句以实现非法操作，执行非授权的任意查询，实质是web应用对用户输入的数据合法性判断不严。</p><p>Statement 采取直接编译 SQL 语句的方式，扔给数据库去执行，而 PreparedStatement 则先将 SQL 语句预编译一遍，再填充参数，这样效率会高一些。</p><p>Statement对象可能进行字符串与变量的拼接，很容易进行 SQL 注入攻击。采用PreparedStatement访问数据库不仅能防止sql注入，还是预编译的，不用重新编译整个sql语句，效率高。此外执行查询语句得到的结果集是离线的，连接关闭后，仍然可以访问结果集。</p><p>注意：PreparedStatement防止sql注入实质是将传入的参数看作字符串处理。${}是拼接，#{}整体当做字符串处理。</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="comment">//删除表中id=1且name=admin的记录</span></span><br><span class="line"></span><br><span class="line">Class.forName(com.mysql.jdbc.Driver);     </span><br><span class="line"><span class="type">Connection</span> <span class="variable">con</span> <span class="operator">=</span> DriverManager.getConnection(</span><br><span class="line">    <span class="string">&quot;jdbc:mysql://localhost:3306/xxx...&quot;</span>); </span><br><span class="line"><span class="comment">//以？作为占位符，值待设置</span></span><br><span class="line"><span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">&quot;delete from user where id=? and name=?&quot;</span>;</span><br><span class="line"><span class="comment">//创建PreparedStatement时就传入sql语句，实现了预编译 </span></span><br><span class="line"><span class="type">PreparedStatement</span> <span class="variable">p</span> <span class="operator">=</span> con.prepareStatement(sql);    </span><br><span class="line"><span class="comment">//传入参数</span></span><br><span class="line">p.setString(<span class="number">1</span>,<span class="string">&quot;1&quot;</span>);</span><br><span class="line">p.setString(<span class="number">2</span>,<span class="string">&quot;admin&quot;</span>);</span><br><span class="line"><span class="comment">//设置sql语句的占位符的值，注意第一个参数位置是1不是0</span></span><br><span class="line">p.execute(); <span class="comment">//执行</span></span><br></pre></td></tr></table></figure><p>同时，mybatis-plus框架也会进行sql的预编译。Mybatis（plus）使用参数化查询，<code>#&#123;xxx&#125;</code> 是参数化查询的占位符，它将传入的参数插入到占位符中，而不会将其解释为 SQL 代码。在自定义查询语句时，为确保参数化查询，须使用 <code>@Param</code> 注解将参数传递给 sql查询。这些措施让Mybatis-plus能防止sql注入。</p><p><a href="https://blog.csdn.net/QGhurt/article/details/107012843">文章：PreparedStatement防止SQL注入原因</a></p><p>​<a href="https://blog.csdn.net/yanghezheng/article/details/133156660">MybatisPlus如何解决SQL注入</a></p><p>​<a href="https://zhuanlan.zhihu.com/p/652655607">Mybatis-plus sql注入及防止sql注入详解</a></p><h4 id="DBCP数据库连接池"><a href="#DBCP数据库连接池" class="headerlink" title="DBCP数据库连接池"></a>DBCP数据库连接池</h4><p>DBCP连接池提高资源利用率，避免重复创建和销毁连接资源，减少系统开销。使用DBCP连接池后，免去了编写连接数据库的代码。使用DBCP需要导入dbcp相关jar包，导入依赖到项目中。</p><figure class="highlight xml"><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">commons-dbcp-1.4.jar</span><br><span class="line">commons-pool-1.5.4jar</span><br></pre></td></tr></table></figure><p>创建多个dbcp.properties文件，存储多个不同的连接池</p><p>连接池的工作原理：<br>①使用连接池前：用jdbc连接数据库时，先getConnection获取连接，用完后要release释放连接，再次连接重复前两步，对程序性能影响很大。<br>②使用连接池后：在程序开始之前，先创建几个连接，将连接放入到连接池中，连接池中缓存了一定量的Connection对象，当用户需要使用连接时，从连接池中获取，使用完毕之后将连接还回连接池。</p><p>连接池的种类：DBCP，C3P0，Druid，Tmcat内置连接池等。</p><p>dhcp配置:</p><figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#导入dbcp的配置文件dbcp.properties</span></span><br><span class="line"><span class="attr">driverclassName</span>=<span class="string">com.mysql.jbcp.Driver</span></span><br><span class="line"><span class="attr">url</span>=<span class="string">jdbc:mysql://localhost:3306/xxx?useUnicode=true</span></span><br><span class="line"><span class="attr">&amp;characterEncoding</span>=<span class="string">utf-8</span></span><br><span class="line"><span class="attr">user</span>=<span class="string">root</span></span><br><span class="line"><span class="attr">password</span>=<span class="string">123456</span></span><br><span class="line"><span class="attr">initialSize</span>=<span class="string">10 #初始连接数量</span></span><br><span class="line"><span class="attr">maxActive</span>=<span class="string">50 #最大活跃连接数量</span></span><br><span class="line"><span class="attr">maxIdle</span>=<span class="string">20 #最大空间连接数</span></span><br><span class="line"><span class="attr">minIdle</span>=<span class="string">5 #最小空间连接数</span></span><br><span class="line"><span class="attr">maxWait</span>=<span class="string">60000 </span></span><br><span class="line"><span class="comment">#最大等待时间ms（毫秒），从连接池获取连接，如果连接池空了，等待的最大时间</span></span><br><span class="line"><span class="attr">connectionProperties</span>=<span class="string">useUnicode=true;characterEncoding=utf-8</span></span><br><span class="line"><span class="attr">defaultAutoCommit</span>=<span class="string">true</span></span><br><span class="line"><span class="attr">defaultTransactionIsolation</span>=<span class="string">READ_COMMITTED</span></span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">InputStream</span> <span class="variable">in</span> <span class="operator">=</span> xxx.class.getClassLoader().</span><br><span class="line">    getResourceAsStream(<span class="string">&quot;dbcp.properties&quot;</span>);</span><br><span class="line"><span class="type">Properties</span> <span class="variable">pro</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Properties</span>();</span><br><span class="line">pro.load(in);</span><br><span class="line"><span class="comment">//声明连接池</span></span><br><span class="line"><span class="keyword">static</span> DataSource datasource=<span class="literal">null</span>;</span><br><span class="line"><span class="comment">//创建连接池</span></span><br><span class="line">datasource=BasicDataSourceFactory.createDataSource(pro);</span><br><span class="line"><span class="comment">//从连接池获取连接</span></span><br><span class="line">conn=datasource.getConnection()；</span><br></pre></td></tr></table></figure><p><a href="https://blog.csdn.net/weixin_43908649/article/details/110239301">DBCP参考资料</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;本文介绍了什么是sql和nosql以及两者区别；介绍了mysql的特点、函数和SQL语句操作数据库；讲解了mysql事务及ACID原则和事务的4种隔离级别，索引的作用和原理；mysql的权限管理和数据备份恢复；数据库的设计和三大范式；SQL注入问题和避免；JDBC以及DBCP连接池连接数据库。</summary>
    
    
    
    <category term="数据库" scheme="https://goodzou.github.io/categories/%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
    
    
  </entry>
  
  <entry>
    <title>linux正则表达式与三剑客</title>
    <link href="https://goodzou.github.io/2023/1211/"/>
    <id>https://goodzou.github.io/2023/1211/</id>
    <published>2023-12-11T09:45:23.000Z</published>
    <updated>2026-02-11T15:24:40.410Z</updated>
    
    <content type="html"><![CDATA[<p>本文介绍了如何使用linux正则表达式结合命令处理文本和awk,grep,sed三种文本工作命令。<span id="more"></span></p><p>正则表达式（regular expression）</p><p>使用一些特殊字符+字母和或数字按照某个规则组合成一 个公式用来表示某个意思这就叫正则表达式。正则表达式是一种方法，很多命令可以采用这种方法：vim ，grep，sed，awk等都支持正则表达式，grep通常打印结果行。</p><h4 id="元字符"><a href="#元字符" class="headerlink" title="元字符"></a>元字符</h4><p>元字符：有特殊含义的字符，可以表示其他的含义</p><h5 id="“-“-开头"><a href="#“-“-开头" class="headerlink" title="“ ^ “ 开头"></a>“ ^ “ 开头</h5><p>匹配后面的某开头字符。</p><p><code>cat a.txt | grep &quot;^a&quot;</code></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">a bc</span><br><span class="line">abc</span><br></pre></td></tr></table></figure><h5 id="“-“-结尾"><a href="#“-“-结尾" class="headerlink" title="“ $ “ 结尾"></a>“ $ “ 结尾</h5><p>匹配前面的某结尾字符。</p><p><code>cat a.txt | grep &quot;b$&quot;</code></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">aa aab</span><br><span class="line">bbb</span><br></pre></td></tr></table></figure><h5 id="“-“单个"><a href="#“-“单个" class="headerlink" title="“ . “单个"></a>“ . “单个</h5><p>匹配任意单个字符，长度不为0。</p><p>“a.{2}” 表示匹配以a开头的后面任意两个单个字符，至少两个，包括空格。</p><p><code>cat a.txt| egrep &quot;a.&#123;2&#125;&quot;</code></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">a b</span><br><span class="line">aaaa  abc</span><br><span class="line">bbb afc</span><br></pre></td></tr></table></figure><p>“a.{4,6}” 表示匹配以a开头的后面任意4 - 6个单个字符，包括空格。</p><p><code>cat a.txt| egrep &quot;a.&#123;4,6&#125;&quot;</code></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">bbb aaaaa</span><br><span class="line">abd efddd</span><br><span class="line">kkk baccccd</span><br></pre></td></tr></table></figure><h5 id="“-“-任意"><a href="#“-“-任意" class="headerlink" title="“ * “ 任意"></a>“ * “ 任意</h5><p>匹配前面的字符出现0次或者任意，贪婪模式, 匹配*号前面的字符任意次，<code>.</code> *表示任意长度任意字符，包括空格。</p><p> <code>cat a.txt| egrep &quot;aa.*&quot;</code></p><figure class="highlight sh"><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">aad da</span><br><span class="line">ABC baa</span><br></pre></td></tr></table></figure><h5 id="“-“-至少一次"><a href="#“-“-至少一次" class="headerlink" title="“ + “ 至少一次"></a>“ + “ 至少一次</h5><p>表示前面字符出现1次以上.</p><p> <code>cat a.txt | egrep &quot;[abc]+&quot;</code></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">abcde</span><br><span class="line">ade</span><br><span class="line">bbb</span><br></pre></td></tr></table></figure><p>相当于 <code>cat a.txt| egrep &quot;[abc]&quot;</code></p><p> <code>cat a.txt | egrep &quot;abc+&quot;</code></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">abc</span><br><span class="line">abccc d</span><br><span class="line">dd abc</span><br></pre></td></tr></table></figure><h5 id="“-“-可有可无"><a href="#“-“-可有可无" class="headerlink" title="“ ? “ 可有可无"></a>“ ? “ 可有可无</h5><p>匹配其前面的字符0或1次,即前面的字符可有可无。</p><p><code>cat a.txt| egrep &quot;ab?&quot;</code></p><figure class="highlight plaintext"><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">ab</span><br><span class="line">a</span><br><span class="line">dd aad</span><br></pre></td></tr></table></figure><h5 id="“-”次数区间"><a href="#“-”次数区间" class="headerlink" title="“{ }”次数区间"></a>“{ }”次数区间</h5><p>注意，在使用grep基本正则表达式要带<code>\</code>转义字符，即<code>\&#123;4,6\&#125;</code>，这里以拓展正则表达式为例，就不用带转义字符。</p><p>{m}    ，匹配前面的字符m次。<br>{m,n} ，匹配前面字符至少m次，最多n次。</p><p><code>cat a.txt| egrep &quot;abcd&#123;2,3&#125;&quot;</code></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">ef abcdd</span><br><span class="line">abcdddef</span><br></pre></td></tr></table></figure><h5 id="“-“或者"><a href="#“-“或者" class="headerlink" title="“ | “或者"></a>“ | “或者</h5><p> <code>cat a.txt| egrep &quot;a|bc&quot;</code></p><figure class="highlight sh"><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">bc as</span><br><span class="line"><span class="built_in">dd</span> bcd</span><br></pre></td></tr></table></figure><h5 id="patten-模式匹配"><a href="#patten-模式匹配" class="headerlink" title="^patten$  模式匹配"></a>^patten$  模式匹配</h5><p> <code>^a$</code>表示只匹配以a这个字符开头和结尾的行。</p><p><code>cat a.txt | egrep &quot;^a$&quot;</code></p><h4 id="“-“-转义"><a href="#“-“-转义" class="headerlink" title="“\“ 转义"></a>“\“ 转义</h4><p>“\“表示是转义字符，就是把元字符转义为普通字符，比如<code>\\</code>表示普通符号”\“，把普通字符转换为特殊意义符号，比如”\n”表示把普通字符n转义为换行符。</p><p>\&lt;或\b    　　　，单词左侧，表示以该单词为词首<br>\&gt;或\b    　　    ，单词右侧，表示以该单词为词尾<br>\&lt;PATTERN\&gt;   ，匹配整个单词</p><h4 id="“-”方括号表达式"><a href="#“-”方括号表达式" class="headerlink" title="“[ ]”方括号表达式"></a>“[ ]”方括号表达式</h4><p>[abc] 表示 a，b，c任意一个，实际上，[abc] &#x3D; “a|b|c”&#x3D;(a|b|c)。</p><p>示例：</p><p>[a-z] 表示小写字母</p><p>[A-Z] 表示大写字母</p><p>[0-Z] 表示数字和字母</p><p><code>egrep (ab|ba) a.txt</code></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">abd efg</span><br><span class="line">acd baef</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><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">([0-9])|([0][0-9])|([1][0-9])</span><br><span class="line">#表示匹配0-9或者00-09或者10-19范围的字符。</span><br></pre></td></tr></table></figure><h5 id="在方括号里面和外面的区别"><a href="#在方括号里面和外面的区别" class="headerlink" title="^在方括号里面和外面的区别"></a>^在方括号里面和外面的区别</h5><p>方括号里面：表示对字符取反，排除某些字符。</p><p>方括号外面：表示以某字符开头。</p><p><u>示例：查找不是以数字开头的行</u></p><p>行开头可能是字母或特殊字符。</p><p><code>cat a.txt| egrep &quot;^[^0-9]&quot;</code></p><p><strong>[]和 {}组合使用</strong></p><p>[0-9]{2} 表示0-9里面可以取2次的意思。</p><p>[0-9]{2,5} 表示从0-9里可以取2-5次的数字，至少2个数字，最多5个。</p><p>[0-9]{2,} 表示从0-9里可以取两次以上，最少2次</p><p>a{2} 表示花括号前面的a连续出现两次。</p><p>abc{2}表示abc中的c出现2次。</p><p><strong>案例：从文本里面过滤出所有邮箱地址</strong></p><p>root@localhost lianxi]# cat mail.txt<br>a  <a href="mailto:&#x61;&#x31;&#64;&#113;&#x71;&#46;&#x63;&#111;&#x6d;">a1@qq.com</a>  aa<br>b <a href="mailto:&#x32;&#98;&#x40;&#115;&#105;&#x6e;&#x61;&#46;&#x63;&#x6f;&#109;">2b@sina.com</a> bb<br>c  <a href="mailto:&#99;&#x33;&#x40;&#49;&#x36;&#x33;&#x2e;&#x63;&#111;&#109;">c3@163.com</a> cc<br>d <a href="mailto:&#x34;&#x64;&#x40;&#x31;&#54;&#51;&#x2e;&#x63;&#111;&#109;">4d@163.com</a> dd</p><p>写一个表示邮箱的正则，过滤邮箱。邮箱地址：字符串1@字符串2.字符串3</p><p>字符串1：a-Z_0-9</p><p>字符串2：0-Z</p><p>字符串3：a-Z</p><p><code>cat mail.txt | grep -o &quot;[0-Z$_]+@[0-z]+.[0-z]+&quot;</code></p><h4 id="awk"><a href="#awk" class="headerlink" title="awk"></a>awk</h4><p>-F，字段分隔符，默认为空格</p><p>-f，从脚本中读取命令</p><p>1）以冒号为分割，显示第一列和第三列的内容：</p><p><code>awk -F ：&#39;&#123;print $1 &quot;\t&quot; $3&#125;&#39; a.txt</code></p><p>2）以冒号为分割，显示UID大于500的用户信息：</p><p><code>awk -F : &#39;$3&gt;500&#39; /etc/passwd</code></p><p>3）以冒号为分割，搜索含root关键字的所有行：</p><p><code>awk -F &#39;/root/&#39; /etc/passwd</code></p><p>4）以冒号为分割，搜索UID大于500的用户，并显示sh</p><p><code>awk -F : &#39;$3&gt;500&#123;print $7&#125;&#39; /etc/passwd</code></p><p>5)BEGIN AND</p><p><code>awk -F : &#39;BEGIN&#123;print &quot;name \t uid&quot;&#125;&#123;print $1 &quot;\t&quot; $3&#125;END&#123;print&quot;from /etc/passwd&quot;&#125;&#39; /etc/passwd</code></p><h4 id="grep"><a href="#grep" class="headerlink" title="grep"></a>grep</h4><p>用法: grep  [选项]  PATTERN [FILE]</p><p>在每个 FILE 或是标准输入中查找 PATTERN。默认的 PATTERN 是一个基本正则表达式(缩写为 BRE)，也可以是拓展正则表达式。</p><p>例如: grep -i ‘hello world’ menu.h main.c</p><p><strong><u>options:</u></strong></p><p>-e :  使用正则表达式，用于指定多个搜索模式。</p><p>-E：使用拓展表达式</p><p>-i :  ignore-case忽略大小写</p><p>-v:  反转匹配。</p><p>-w, –word-regexp ，强制 PATTERN 仅完全匹配字词。</p><p>-x：完全匹配整行内容，包括行首行尾空格内容都要完全匹配。</p><p>-f :  指定规则文件，其内容含有一个或多个规则样式，让grep查找符合规则条件的文件内容，格式为每行一个规则样式。</p><p>-c： 输出每个文件所匹配到的行数。</p><p>-r：递归搜索匹配内容</p><p>-L：列出不满足匹配要求的文件名，不输出行内容</p><p>-l ： 列出满足匹配要求的文件名，不输出行内容。</p><p>-z ：匹配一个 0 字节的数据行，但不是空行。</p><p>-m n：当匹配内容的行数达到n行后停止搜索,并输出停止前搜索到的匹配内容。</p><p>-o: 只输出匹配的具体字符串,匹配行中其他内容不会输出。</p><p>-q：安静模式,不会有任何输出内容,<code>echo $?</code>查找到匹配内容会返回0,未查找到匹配内容就返回非0</p><p>-s：不会输出查找过程中出现的任何错误消息。</p><p><u>输出线前缀控制：</u></p><p>-b：输出每一个匹配行或字符串时附上偏移量(从文件第一个字符到该匹配内容之间的字节数)</p><p>-n：输出匹配内容的同时输出其所在行号。</p><p>-H：在每一个匹配行之前加上文件名一起输出。</p><p>-h：不带文件名输出。</p><p>-T：在匹配信息和其前的附加信息之间加入tab以使格式整齐。</p><p><u>上下文线控制选项：</u></p><p>-A n：匹配到搜索到的行以及该行下面的n行</p><p>-B n：匹配到搜索到的行以及该行上面的n行</p><p>-C n：匹配到搜索到的行以及上下各n行</p><p>案例1：在&#x2F;home目录下查找所有内容含abc的文件名</p><p><code>grep -rl abc * </code></p><p>案例2：统计在passwd和shadow文件中含root的行数</p><p><code>grep -c root /etc/passwd /etc/shadow</code></p><h4 id="sed"><a href="#sed" class="headerlink" title="sed"></a>sed</h4><p> sed 全名为 stream editor，流编辑器，用程序的方式来编辑文本，功能相当的强大。是贝尔实验室的 Lee E.M 在 1974年开发完成，目前可以在大多数操作系统中使用。与vim等编辑器不同，sed 是一种非交互式编辑器，它使用预先设定好的编辑指令对输入的文本进行编辑，完成之后再输出编辑结构。</p><p>sed会一次处理一行内容，处理时，把当前处理的行存储在临时缓冲区中，成为”模式空间”pattern space，接着用sed命令处理缓冲区中的内容，处理完成后，把缓冲区的内容送往屏幕。接着处理下一行，这样不断重复，直到文件末尾，文件内容并没有改变。</p><p><strong>用法</strong>：sed [options]  ‘command’   file，options有-n，-e，-i，-f，-r选项。</p><p>-n      使用安静模式，加入-n 后只打印被匹配的行。<br>-e      多重编辑，命令顺序会影响结果。<br>-r       使用扩展正则表达式。<br>-i       将操作结果写入文档，不在屏幕上输出。<br>-f       指定一个 sed 脚本文件到命令行执行。</p><p>COMMNAD:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">a        在当前行后添加一行或多行</span><br><span class="line">c        用新文本修改（替换）当前行中的文本</span><br><span class="line">d        删除行</span><br><span class="line">i        在当前行之前插入文本</span><br><span class="line">p        从暂存区打印行</span><br><span class="line">s        用一个字符串替换另一个</span><br><span class="line">h        把模式空间里的内容复制到暂存缓存区</span><br><span class="line">H        把模式空间里的内容追加到暂存缓存区</span><br><span class="line">g       取出暂存缓冲区里的内容，将其复制到模式空间，覆盖该处原有内容</span><br><span class="line">G       取出暂存缓冲区里的内容，将其复制到模式空间，追加在原有内容后面</span><br><span class="line">l        列出非打印字符</span><br><span class="line">n       读入下一输入行，并从下一条命令而不是第一条命令开始处理</span><br><span class="line">q        结束或退出 sed</span><br><span class="line">r        从文件中读取输入行</span><br><span class="line">！       对所选行之外的所有行应用命令</span><br></pre></td></tr></table></figure><p>1）安静打印</p><p><code>sed -n &#39;p&#39; a.txt</code></p><p>或<code>sed &#39;r&#39; a.txt</code></p><p>2）每行打印两遍输出</p><p><code>sed &#39;p&#39; a.txt</code></p><p>3）只输出第2到5行，原文件内容不变</p><p><code>sed -n &#39;2,5p&#39; a.txt</code></p><p>4）打印第4行后退出</p><p><code>sed &#39;4q&#39; a.txt</code></p><p>5）<u>搜索</u>指定内容并打印</p><p>找到含abc的行并把该行输出：</p><p><code>sed -n &#39;/abc/p&#39; a.txt</code></p><p>注意与<code>sed &#39;/abc/p&#39; a.txt</code>的区别，这是全部输出并把这行打印2遍。</p><p>6）<u>删除</u></p><p>删除第2,到4行的数据：</p><p><code>sed &#39;2,4d&#39; a.txt</code></p><p>删除含abc的行：</p><p><code>sed &#39;/abc/d&#39; a.txt</code></p><p>7）追加a和插入i</p><p>在第二行下插入abc</p><p><code>sed &#39;2a abc&#39; a.txt</code></p><p>在第二行上插入abc</p><p><code>sed &#39;2i abc&#39; a.txt</code></p><p>8）整行替换</p><p>替换2,3,4行为abc</p><p><code>sed &#39;2,4c abc&#39; a.txt</code></p><p>9）字符串替换</p><p>全局替换将文件中的abc替换为a字符,g(global)</p><p><code>sed &#39;s/abc/a/g&#39; a.txt</code></p><p>仅换将文件中第3行的abc替换为a字符</p><p><code>sed &#39;3s/abc/a/g&#39; a.txt</code></p><p><u><strong>多条件替换</strong></u>：</p><p>将abc和def替换为a字符</p><p><code>sed -e &#39;s/abc/a/g;s/def/a/g&#39; a.txt</code></p><p>10）结果写入</p><p>使用-i选项，将原本输出到屏幕的结果写入文档。</p><p><code>sed -i &#39;/abc/p&#39; a.txt</code></p><p>11）从文件读入</p><p>子命令r，类似于a，也是将内容追加到指定行的后边，只不过r是将指定文件内容读取并追加到指定行下边。 </p><p>例：<code>sed &#39;2r b.txt&#39; a.txt</code></p><p>将b.txt文件内容读取并插入到a.txt文件第2行的下边。</p><h4 id="其他文本处理命令"><a href="#其他文本处理命令" class="headerlink" title="其他文本处理命令"></a>其他文本处理命令</h4><h5 id="nl-编写行号"><a href="#nl-编写行号" class="headerlink" title="nl 编写行号"></a>nl 编写行号</h5><p>nl  [options]  [file]，默认空行不统计</p><p>-b，选择样式，a所有、t非空行、n不显示、BRE正则(正则匹配的行才显示行号)</p><p>-n，–number-format&#x3D;格式，根据指定格式插入行号。</p><p> -w, –number-width&#x3D;数字，为行号使用指定的栏数。</p><p>-s, –number-separator&#x3D;字符串，可能的话在行号后添加字符串。</p><p>格式是下列之一:</p><p>  ln左对齐，空格不用0 填充<br>  rn 右对齐，空格不用0 填充<br>  rz 右对齐，空格用0 填充</p><p><code>nl -b a a.txt</code>，统计a.txt中所有行，</p><h5 id="wc词数统计"><a href="#wc词数统计" class="headerlink" title="wc词数统计"></a>wc词数统计</h5><p>wc [options]  [file]</p><p><code>wc a.txt</code></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">7 14 74 a.txt</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">行数 字（单词）数 字节数</span></span><br><span class="line"><span class="meta prompt_">$</span><span class="language-bash">1 1111 aaa</span></span><br><span class="line"><span class="meta prompt_">$</span><span class="language-bash">2 22222 bbb</span></span><br><span class="line"><span class="meta prompt_">$</span><span class="language-bash">3 33333  cccc</span></span><br><span class="line"><span class="meta prompt_">$</span><span class="language-bash">4 44444 ddd</span></span><br><span class="line"><span class="meta prompt_">$</span><span class="language-bash">5 bbb aaaaa</span></span><br><span class="line"><span class="meta prompt_">$</span><span class="language-bash">6 abd efddd</span></span><br><span class="line"><span class="meta prompt_">$</span><span class="language-bash">7 kkk baccccd</span></span><br></pre></td></tr></table></figure><p>options:</p><p>-w，只统计文件中单词数</p><p>-m，字符数</p><p>-c，统计字节数</p><p>-L，显示最长行长度（可见字符加空格）</p><p>-l，行数</p><h5 id="sort"><a href="#sort" class="headerlink" title="sort"></a>sort</h5><p><code>sort a.txt</code>，文本排序，默认按首字母、第一个数字顺序排序。</p><p>options:</p><p>-n，按数值大小排序。</p><p>-r，逆序排列。</p><p>-t，指定分隔符</p><p>-k，选取分隔后的哪一列</p><p>案例：按a.txt文件中第三列数字大小进行排序输出</p><p><code>sort -k 3 -n a.txt</code></p><h5 id="uniq"><a href="#uniq" class="headerlink" title="uniq"></a>uniq</h5><p><code>uniq a.txt</code>，去连续重行。</p><p>-d，只打印重复的行</p><p>-u，只打印不重复的行</p><p>-c，统计重复行的出现次数</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;本文介绍了如何使用linux正则表达式结合命令处理文本和awk,grep,sed三种文本工作命令。</summary>
    
    
    
    <category term="linux" scheme="https://goodzou.github.io/categories/linux/"/>
    
    
  </entry>
  
  <entry>
    <title>Git入门</title>
    <link href="https://goodzou.github.io/2023/1201/"/>
    <id>https://goodzou.github.io/2023/1201/</id>
    <published>2023-12-01T07:16:09.000Z</published>
    <updated>2026-02-11T15:22:46.996Z</updated>
    
    <content type="html"><![CDATA[<p>本文介绍的git版本控制、配置、命令操作、分支和文件状态等内容<span id="more"></span></p><h4 id="版本控制"><a href="#版本控制" class="headerlink" title="版本控制"></a>版本控制</h4><p>本地版本控制：在本地记录每一次版本更新。</p><p>集中版本控制：版本数据都保存在单一服务器，不联网就看不到版本信息。SVN</p><p>分布式版本控制：所有的版本信息都同步到本地的每个用户，可以离线在本地提交，只需在联网时push。Git</p><p><u>SVN与Git的区别：</u></p><ul><li>SVN 是集中式的；Git 是分布式的</li><li>SVN 的分支操作成本（创建&#x2F;删除&#x2F;合并）比 Git 高</li><li>SVN 是存储变更差异；Git 是存储文件快照</li><li>SVN必须联网操作，只有服务器上的版本控制；Git支持离线操作，有本地的版本控制</li></ul><h4 id="git配置"><a href="#git配置" class="headerlink" title="git配置"></a>git配置</h4><p>1）git\mingw64\etc\gitconfig ，–system系统级</p><p>2）C:\Users\用户\.gitconfig ，–global全局</p><p>Git branch 获取分支列表 ，列表保存到refs&#x2F;heads&#x2F;master 下面。</p><p><code>git config -l</code>，配置清单</p><p><code>git config --system --list</code>，系统配置</p><p><code>git config --global --list</code>，全局配置（用户配置）</p><p>安装好git后，设置用户名和邮箱（必要）：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git config --global user.name &quot;xxx&quot;</span><br><span class="line">git config --global user.email &quot;xxx@qq.com&quot;</span><br></pre></td></tr></table></figure><p><strong>配置ssh公私钥连接远程仓库：</strong></p><p>在c:&#x2F;Users&#x2F;用户&#x2F;.ssh下打开git bash，运行<code>ssh-kengen</code>生成公私钥，将pub公钥粘贴到远程仓库配置中。</p><h4 id="工作区域"><a href="#工作区域" class="headerlink" title="工作区域"></a>工作区域</h4><p>git本地有四个工作区域：工作目录、暂存区(stage&#x2F;index)、资源库(repository)、远程仓库(remote repository)。</p><p>工作区：平时写代码项目文件的目录。</p><p>index&#x2F;stage：存放提交的文件列表信息的文件。</p><p>Local R本地仓库：存放提交的所有版本信息，HEAD指针总是指向当前分支。</p><p>Remote：托管代码的远程服务器。</p><h4 id="文件状态"><a href="#文件状态" class="headerlink" title="文件状态"></a>文件状态</h4><p>untracked：未加入到本地库中，不参与版本控制，使用git add变为staged状态</p><p>unmodify：文件已入库，未修改。被修改则变为modified文件或git rm移除版本库变为untracked状态。</p><p>modified：文件已修改。通过git add变为staged状态，或通过git checkout放弃修改回到unmodify状态。</p><p>staged：暂存状态。执行git commit提交到版本库中，文件变为unmodify状态，执行git reset HEAD filename取消暂存，文件变为modified状态。</p><h4 id="git对象模型"><a href="#git对象模型" class="headerlink" title="git对象模型"></a>git对象模型</h4><p>在git系统中有四种对象：   </p><p>commit：指向一个tree，纪录了文件操作，作者，提交者信息；  </p><p>tree：对象关系树，管理tree和blob的关系；   </p><p>blob：保存文件内容；   </p><p>tag：标记提交。</p><h4 id="基础命令"><a href="#基础命令" class="headerlink" title="基础命令"></a>基础命令</h4><p><code>git init</code>，本地创建一个新的项目。</p><p><code>git clone [url]</code>，克隆远程仓库上的项目代码。</p><p><code>git rm/mv [file]</code>，删除移动文件</p><p><code>git status [xx]</code>，查看文件状态。</p><p><code>git commit -m xxx</code>，提交暂存区中的内容到本地仓库，-v提交时显示diff信息。</p><p><code>git branch</code>，查看本地分支，-r查看远程，-d [name]删除分支，-rd删除远程分支</p><p><code>git branch [bname]</code>，新建分支</p><figure class="highlight plaintext"><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><br><span class="line">$ git branch --track [branch] [remote-branch]</span><br></pre></td></tr></table></figure><p><code>git merge [branch]</code>，合并分支到当前分支。</p><p><code>git checkout [branch]或git switch</code>，切换到指定分支，实际是移动HEAD指针。</p><p><code>git checkout [file]</code>，恢复暂存区文件到工作区。</p><p><code>git reset --hard [版本号]</code>，版本穿梭(切换版本)。</p><p><code>git log</code>，显示当前分支版本历史，-S [key]根据关键词搜索提交历史，–follow [file]显示某文件的版本历史，–oneline简洁显示。</p><p><code>git reflog</code>，显示当前分支的最近几次提交。</p><p><code>git shortlog -sn</code>，显示所有提交过的用户。</p><p><code>git blame [file]</code>，显示文件被什么人什么时间修改。</p><p><code>git diff</code>，显示工作区和暂存区的差异。</p><p><code>git show [commit]</code>，显示某次提交的时间和内容变化。</p><p><code>git fetch [remote]</code>，下载远程仓库变动到本地版本库。</p><p><code>git remote -v</code>，显示所有远程仓库，add添加仓库，remove删除。</p><p><code>git pull [remote] [branch]</code>，取回远程仓库分支的变化并与本地合并。</p><p><code>git push [remote] [branch]</code>，推送本地分支到远程仓库。</p><p>master分支主要用于发布新版本，常用工作分支使用dev等分支。</p><p>推送失败，因为最新提交和你试图推送的提交有冲突，两者对同一文件同一地点进行了<strong>修改</strong>。先用git pull把最新提交从origin&#x2F;dev抓下来，然后在本地合并，解决冲突再推送或者进行git reset回滚。git pull也失败了，原因是没有指定本地dev分支与远程origin&#x2F;dev分支的链接，根据提示，设置dev和origin&#x2F;dev的链接。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git branch --set-upstream-to=origin/dev dev</span><br></pre></td></tr></table></figure><p>这回git pull成功，但是合并有冲突，需要手动合并，即vim编辑冲突的文件，删除特殊符号，决定保留哪些内容，wq保存后，提交暂存区，提交本地库，再push。</p><h4 id="gitignore忽略文件"><a href="#gitignore忽略文件" class="headerlink" title=".gitignore忽略文件"></a>.gitignore忽略文件</h4><p>在目录下创建.gitignore文件，将某些文件排除在版本控制外。</p><p><code>#</code>：注释。</p><p><code>tmp/*.txt</code>：忽略tmp目录所有以.txt结尾的文件，不包括<code>tmp/d/*.txt</code>。</p><p><code>!</code>：除了这个文件外忽略所有</p><p><code>/tmp</code>：忽略除tmp文件夹外的其他文件</p><p><code>tmp/</code>：忽略tmp目录下的所有文件</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">HELP.md</span><br><span class="line">target/</span><br><span class="line">!.mvn/wrapper/maven-wrapper.jar</span><br><span class="line">!**/src/main/**/target/</span><br><span class="line">!**/src/test/**/target/</span><br><span class="line"></span><br><span class="line">### STS ###</span><br><span class="line">.apt_generated</span><br><span class="line">.classpath</span><br><span class="line">.factorypath</span><br><span class="line">.project</span><br><span class="line">.settings</span><br><span class="line">.springBeans</span><br><span class="line">.sts4-cache</span><br><span class="line"></span><br><span class="line">### IntelliJ IDEA ###</span><br><span class="line">.idea</span><br><span class="line">*.iws</span><br><span class="line">*.iml</span><br><span class="line">*.ipr</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="IDEA集成Git"><a href="#IDEA集成Git" class="headerlink" title="IDEA集成Git"></a>IDEA集成Git</h4><p>将所有克隆下来的git项目或新创建的git项目文件拷贝到IDEA项目中即可。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;本文介绍的git版本控制、配置、命令操作、分支和文件状态等内容</summary>
    
    
    
    <category term="git" scheme="https://goodzou.github.io/categories/git/"/>
    
    
  </entry>
  
  <entry>
    <title>docker实战</title>
    <link href="https://goodzou.github.io/2023/1122/"/>
    <id>https://goodzou.github.io/2023/1122/</id>
    <published>2023-11-22T03:38:31.000Z</published>
    <updated>2026-02-11T15:22:33.773Z</updated>
    
    <content type="html"><![CDATA[<p>本文将讲述如何使用docker制作tomcat镜像并上传至阿里云，部署redis和springboot项目。<span id="more"></span></p><h4 id="制作tomcat镜像"><a href="#制作tomcat镜像" class="headerlink" title="制作tomcat镜像"></a>制作tomcat镜像</h4><p>通过dockerfile制作一个tomcat镜像并发布到阿里云镜像仓库。</p><p>1、在用户工作的主目录中创建一个build&#x2F;tomcat文件夹</p><p>2、准备tomcat和jdk安装包上传到该文件夹下</p><p>3、在文件夹下创建一个README.txt文件并编写Dockerfile文件</p><p><code>touch README.txt</code></p><p><code>vim Dockerfile</code></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">FROM centos#本地基础镜像docker images查看是否有该镜像否则将失败</span><br><span class="line">MAINTAINER zzh&lt;xxxx@qq.com&gt;</span><br><span class="line">COPY README.txt /usr/local/</span><br><span class="line">ADD /root/build/tomcat/apache-tomcat-8.5.94.tar.gz /usr/local/</span><br><span class="line">ADD /root/build/tomcat/jdk-8u151-linux-x64.tar.gz /usr/local/</span><br><span class="line"></span><br><span class="line">RUN yum -y install vim</span><br><span class="line">RUN yum -y install net-tools</span><br><span class="line"></span><br><span class="line">ENV MYPATH /usr/local</span><br><span class="line">WORKDIR $MYPATH</span><br><span class="line"></span><br><span class="line">ENV JAVA_HOME /usr/local/jdk1.8.0_151</span><br><span class="line">ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools/jar</span><br><span class="line">ENV CATALINA_HOME /usr/local/apache-tomcat-8.5.94</span><br><span class="line">ENV CATALINA_BASH /usr/local/apache-tomcat-8.5.94</span><br><span class="line">ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin</span><br><span class="line"></span><br><span class="line">EXPOSE 8080</span><br><span class="line"></span><br><span class="line">CMD /usr/local/apache-tomcat-8.5.94/bin/startup.sh &amp;&amp; tail -F /usr/local/apache-tomcat-8.5.94/logs/catalina.out</span><br></pre></td></tr></table></figure><p>4、构建diytomcat镜像</p><p><code>docker build -t diytomcat .</code></p><p>5、创建容器运行</p><p><code>docker run -d -p 3355:8080 --name mytomcat01 -v /root/build/tomcat/test:/usr/local/apache-tomcat-8.5.94/webapps/test -v /root/build/tomcat/logs:/usr/local/apache-tomcat-8.5.94/logs diytomcat</code></p><p>6、测试运行</p><p>在linux主机测试：curl localhost:3355或直接访问。</p><p>7、上传至阿里云镜像仓库</p><p><code>docker login --username=xxx registry.cn-shanghai.aliyuncs.com</code></p><p>#重命名镜像</p><p><code>docker tag 镜像 registry.cn-shanghai.aliyuncs.com/命名空间/仓库名:tag</code></p><p><code>docker push registry.cn-shanghai.aliyuncs.com/命名空间/仓库名:tag</code></p><h4 id="Redis集群部署"><a href="#Redis集群部署" class="headerlink" title="Redis集群部署"></a>Redis集群部署</h4><p>1、创建网络</p><p><code>docker network create redis --subnet 172.17.0.0/16</code></p><p>2、通过脚本创建redis配置</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">for port in $(seq 1 6); \</span><br><span class="line">do \</span><br><span class="line">mkdir -p /mydata/redis/node-$&#123;port&#125;/conf</span><br><span class="line">touch /mydata/redis/node-$&#123;port&#125;/conf/redis.conf</span><br><span class="line">cat &lt;&lt; EOF &gt;/mydata/redis/node-$&#123;port&#125;/conf/redis.conf</span><br><span class="line">port 6379</span><br><span class="line">bind 0.0.0.0</span><br><span class="line">cluster-enabled yes</span><br><span class="line">cluster-config-file node.conf</span><br><span class="line">cluster-node-timeout 5000</span><br><span class="line">cluster-announce-ip 172.17.0.1$&#123;port&#125;</span><br><span class="line">cluster-announce-port 6379</span><br><span class="line">cluster-announce-bus-port 16379</span><br><span class="line">appendonly yes</span><br><span class="line">EOF</span><br><span class="line">done</span><br><span class="line"></span><br><span class="line">docker run -p 637$&#123;port&#125;:6379 -p 16371:16379 --name redis-$&#123;port&#125; \</span><br><span class="line">-v /mydata/redis/node-$&#123;port&#125;/data:/data \</span><br><span class="line">-v /mydata/redis/node-$&#123;port&#125;/conf/redis.conf:/etc/redis/redis.conf \</span><br><span class="line">-d --net redis --ip 172.17.0.1$&#123;port&#125; redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf</span><br><span class="line"></span><br></pre></td></tr></table></figure><p><code>docker run -p 6371:6379 -p 16371:16379 --name redis-1 \ -v /mydata/redis/node-1/data:/data \ -v /mydata/redis/node-1/conf/redis.conf:/etc/redis/redis.conf \ -d --net redis --ip 172.17.0.11 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf</code></p><p>3、查看redis容器，进入容器</p><p><code>docker ps</code></p><p><code>docker exec -it redis-1 /bin/sh</code></p><p>3、创建redis集群</p><p><code>redis-cli --cluster create 172.17.0.11:6379 172.17.0.12:6379 172.17.0.13:6379 172.17.0.14:6379 172.17.0.15:6379 172.17.0.16:6379 --cluster-relicas 1 </code></p><p>4、查看集群</p><p><code>redis-cli -c</code></p><p><code>cluster info</code></p><p><code>cluster nodes</code></p><h4 id="SpringBoot微服务打包docker镜像"><a href="#SpringBoot微服务打包docker镜像" class="headerlink" title="SpringBoot微服务打包docker镜像"></a>SpringBoot微服务打包docker镜像</h4><p>1、将springboot项目打包为xxx.jar</p><p>2、编写Dockerfile文件</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">FROM java:8</span><br><span class="line">MAINTAINER z&lt;xx@qq.com&gt;</span><br><span class="line"></span><br><span class="line">COPY *.jar  /app.jar</span><br><span class="line">EXPOSE 8080</span><br><span class="line"></span><br><span class="line">ENTRYPOINT [&quot;java&quot;,&quot;-jar&quot;,&quot;/app.jar&quot;]</span><br></pre></td></tr></table></figure><p>3、构建镜像</p><p><code>docker build -t myApp .</code></p><p>4、发布镜像，下载镜像运行</p><p><code>docker push myApp</code></p><p><code>docker pull myApp</code></p><p><code>docker run -d -p 8080:8080 --name X-WEB-APP myApp</code></p><p>5、测试</p><p><code>curl localhost:8080</code></p><h4 id="拓展"><a href="#拓展" class="headerlink" title="拓展"></a>拓展</h4><p>compose、swarm、Jenkins</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;本文将讲述如何使用docker制作tomcat镜像并上传至阿里云，部署redis和springboot项目。</summary>
    
    
    
    <category term="docker" scheme="https://goodzou.github.io/categories/docker/"/>
    
    
  </entry>
  
  <entry>
    <title>docker入门</title>
    <link href="https://goodzou.github.io/2023/1121/"/>
    <id>https://goodzou.github.io/2023/1121/</id>
    <published>2023-11-21T02:17:40.000Z</published>
    <updated>2026-04-20T07:03:15.641Z</updated>
    
    <content type="html"><![CDATA[<p>什么是Docker</p><p>Docker是内核级别的虚拟化，可以在一个物理机上可以运行很多的容器实例。服务器的性能可以被压榨到极致。Docker是基于Go语言开发的，开源项目。<span id="more"></span></p><p><strong>镜像（image）：</strong><br>docker镜像就好比是一个模板，可以通过这个模板来创建容器服务，镜像&#x3D;&gt;run&#x3D;&gt;容器，通过这个镜像可以创建多个容器（最终服务运行或者项目运行就是在容器中的）。</p><p><strong>容器（container）：</strong><br>Docker利用容器技术，独立运行一个或者一个组应用，通过镜像来创建的。启动，停止，删除。</p><p><strong>仓库（repository）：</strong><br>仓库就是存放镜像的地方。</p><p>官网：<a href="https://www.docker.com/">https://www.docker.com/</a><br>文档地址：<a href="https://docs.docker.com/">https://docs.docker.com/</a><br>Doker镜像仓库地址：<a href="https://hub.docker.com/">https://hub.docker.com/</a></p><h4 id="Linux下安装docker"><a href="#Linux下安装docker" class="headerlink" title="Linux下安装docker"></a>Linux下安装docker</h4><p>1、需要的安装包</p><p>yum install -y yum-utils</p><p>2、设置镜像的仓库</p><p><code>yum config-manager  --add-repo  http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo</code></p><p>3、更新yum软件包索引。</p><p>yum makecache</p><p>4、安装</p><p><code>yum install docker-ce docker-ce-cli containerd.io</code></p><p>5、启动docker</p><p>systemctl start docker&#96;</p><p>6、使用docker version查看是否安装成功</p><p>docker run hello-world</p><h4 id="配置阿里云镜像加速"><a href="#配置阿里云镜像加速" class="headerlink" title="配置阿里云镜像加速"></a>配置阿里云镜像加速</h4><p>登录阿里云，找到容器服务。找到镜像加速地址，配置使用。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">sudo mkdir -p /etc/docker</span><br><span class="line">sudo tee /etc/docker/daemon.json   &lt;&lt;-&#x27;EOF&#x27;</span><br><span class="line">&#123;</span><br><span class="line">&quot;registry-mirrors&quot;: [&quot;https://xxx.xxx.xxx.com&quot;]</span><br><span class="line">&#125;</span><br><span class="line">EOF</span><br><span class="line">sudo systemctl daemon-reload</span><br><span class="line">sudo systemctl restart docker</span><br></pre></td></tr></table></figure><h4 id="docker命令"><a href="#docker命令" class="headerlink" title="docker命令"></a>docker命令</h4><p><code>docker --help</code> 帮助</p><p><code>docker version</code> 版本</p><p><code>docker info</code> 详细信息</p><p><code>docker stats</code>实时查看容器运行情况 </p><p><code>docker history IMAGEID</code>查看镜像构建记录</p><p><strong>镜像命令</strong>：</p><p><code>docker images  [-a所有镜像,-q只要镜像ID]</code>查看本机镜像</p><p><code>docker tag 镜像id 新镜像名:tag标签</code> 克隆命名新的镜像</p><p><code>docker search xxx </code> 搜素镜像</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker search mysql --filter=STARS=3000</span><br></pre></td></tr></table></figure><p>[root@CentOS7 docker]# docker search mysql –filter&#x3D;STARS&#x3D;3000<br>NAME      DESCRIPTION                                      STARS     OFFICIAL   AUTOMATED<br>mysql     MySQL is a widely used, open-source…   14616     [OK]<br>mariadb   MariaDB Server is a high performing…   5573      [OK]   </p><p><code>docker pull xx</code>  <strong><u>下载镜像</u></strong>，默认最新版（分层下载，节省开销，加快速度）</p><p>指定版本下载：docker pull mysql:5.7  </p><p><code>docker rmi [-f] 镜像名/镜像ID</code>  删除镜像</p><p>**<u>删除所有镜像</u>**文件： <code>docker rmi -f $(docker images -aq)</code> </p><p><strong><u>生成镜像</u></strong></p><p><code>docker commit 容器id xx镜像名</code>，根据容器的changes生成一个镜像，-a，作者名；-m，信息。</p><p><code>docker build [options] 生成路径path</code>，根据dockerfile生成镜像。</p><p>docker build [选项] 镜像名[:标签]或者上下文路径或者URL  所执行目录（.表示在当前目录执行）</p><p>例：<code>docker build -f mydockerfile -t mytomcat .</code></p><p><u><strong>上传提交镜像</strong></u></p><p><code>docker push [OPTIONS] NAME[:TAG]</code>，上传一个镜像到远程仓库。</p><p>Options:<br>  -a, –all-tags，将所有版本的镜像提交。<br>      –disable-content-trust   忽略镜像提示。<br>  -q, –quiet  ，简洁生成。</p><p><u>上传到dockerhub</u></p><p>1）在hub.docker.com创建账号</p><p>2）登录账户：docker login -u  xxx  -p </p><p>3）上传镜像</p><p><code>docker push 镜像:tag</code></p><p><u>上传到阿里云</u></p><p>1）登录到阿里云，找到容器镜像服务</p><p>2）创建命名空间</p><p>3）创建容器镜像仓库</p><p>4）查看仓库基本信息，获取ip地址，按照操作指南执行</p><p>5）比如登录：</p><p><code>sudo docker login --usename xxx registry.cn-beijing.aliyuncs.com</code></p><p><strong>容器命令</strong>：</p><p>有了镜像才能创建容器。</p><p><strong><u>运行容器</u></strong></p><p><code>docker run [options] 镜像 </code>，创建容器运行镜像</p><p><code>--name</code>， 命名容器，Repository:Tag</p><p><code>--rm</code>，退出时删除容器</p><p><code>-d</code> ，以后台方式运行，若容器不向客户端提供服务则停止运行</p><p><code>-it</code> ，交互方式运行，启动进入容器查看内容</p><p>exit或ctrl+d退出停止运行；ctrl + p + q退出不停止</p><p><code>-p</code> ，指定容器端口，主机端口：容器端口</p><p>例1：交互式运行centos</p><p><code>docker run -it centos /bin/bash</code> #会进入容器的交互终端</p><p><code>docker run -itd centos /bin/bash</code> #以后台交互方式运行容器，不会进入容器</p><p>后面的&#x2F;bin&#x2F;bash的作用是表示运行bash ,docker中必须要保持一个进程的运行，这个&#x2F;bin&#x2F;bash就表示启动容器后启动bash。</p><p>例2：以脚本方式运行centos镜像，每隔1秒启动容器。</p><p><code>docker run -d centos /bin/bash -c &quot;while true;do echo xxx;sleep 1 ; done&quot;</code></p><p>例3：以后台方式创建容器运行nginx镜像，并向外开放3344端口</p><p><code>docker run -d --name nginx01 -p 3344:80 nginx</code></p><p><strong><u>查看容器</u></strong></p><p><code>docker ps  [options]</code>，查看正在运行的容器</p><p><code>-a</code>查看运行记录，<code>-n=?</code>显示最近运行的容器，<code>-q</code>显示ID</p><p><code>docker container ls</code></p><p><strong><u>删除容器</u></strong></p><p><code>docker rm 容器id</code>，删除容器；-f，强制删除</p><p>例：删除所有容器</p><p><code>docker rm -f $(docker ps -aq)</code></p><p><strong><u>启动和停止容器</u></strong></p><p><code>docker start 容器id</code></p><p><code>docker restart 容器id</code></p><p><code>docker stop 容器id</code> </p><p><code>docker kill id</code>，强制停止运行</p><p><strong><u>（生成）镜像</u></strong></p><p><code>docker container commit 容器id xx镜像名</code>，<code>-a</code>，作者名，<code>-m</code>，信息。</p><p><code>docker build [options] 生成路径path</code>，根据dockerfile生成镜像。</p><p>docker build [选项] 镜像名[:标签]或者上下文路径或者URL  所执行目录（.表示在当前目录执行）</p><p>-f指定dockerfile文件，-t命名镜像</p><p>例1：提交容器3fdf3rfe4生成镜像tomcat01</p><p><code>docker commit -a=&quot;xxx&quot; -m=&quot;xxx&quot; 3fdf3rfe4 tomcat01:1.0</code></p><p>例2：根据dockerfile生成镜像保存到当前目录</p><p><code>docker build -f /home/dockerfile -t myimage:1.0 .</code></p><p><strong>其他命令：</strong></p><p><code>docker logs [options] 容器id</code>，查看日志</p><p>Options:<br> –details，显示详细信息<br>-f, –follow  ，实时输出日志<br>–since ，显示在某个时间后的日志<br>-n, –tail ，展示最后n行日志<br>-t, –timestamps   ,显示时间<br>–until  ,显示一个时间前的日志</p><p><code>docker top 容器id</code>，查看<u>正在运行的容器中</u>的进程</p><p><code>docker inspect 容器id</code>，查看容器的元数据(返回的JSON格式)</p><p><code>docker exec -it 容器id /bin/bash</code>，以新终端进入正在运行的容器</p><p><code>docker attach 容器id</code>，进入容器正在运行的终端</p><p><code>docker cp 容器id:文件绝对路径 主机目的路径</code>，从容器中拷贝文件到主机</p><p><code>docker pause 容器id</code>  悬挂中断</p><p><code>docker unpause 容器id</code>  </p><h4 id="部署安装Tomcat、ES"><a href="#部署安装Tomcat、ES" class="headerlink" title="部署安装Tomcat、ES"></a>部署安装Tomcat、ES</h4><p><u>tomcat</u></p><p>1、下载镜像</p><p><code>docker pull tomcat:9.0</code></p><p>2、启动镜像</p><p><code>docker run -d --name tomcat01 -p 3355:8080 tomcat:9.0</code></p><p>3、进入tomcat容器中，配置&#x2F;usr&#x2F;local&#x2F;tomcat&#x2F;webapps目录</p><p><code>docker exec -it tomcat01 /bin/bash</code></p><p><code>cp -r webapps.dist webapps</code></p><p>4、访问测试</p><p><code>docker ps</code></p><p><code>curl localhost:3355</code></p><p><u>ES</u></p><p><code>docker run -d  --name es01 -p 9200:9200 -p 9300:9300 -e &quot;discovery.type=single-node&quot; -e ES_JAVA_OPTS=&quot;-Xms64m -Xmx512m&quot; elasticsearch:7.7.0</code></p><h4 id="容器数据卷"><a href="#容器数据卷" class="headerlink" title="容器数据卷"></a>容器数据卷</h4><p>让容器内的文件数据与linux主机或容器之间同步互通，形成共享，修改容器配置文件时可直接在主机服务器上修改，不需要进入容器，实现容器数据持久化。</p><p><strong>方式一、-v卷挂载命令</strong></p><p>例：<code>docker run -it -v 主机路径:容器内路径 centos /bin/bash</code></p><p>在主机上通过docker inspect 容器，查看mount挂载情况。</p><p>例：安装mysql同步数据</p><p><code>docker pull mysql:5.7</code></p><p><code>docker run -d -p 3304:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql_01 mysql:5.7</code></p><p>参数说明：-d后台运行，-p端口映射，-v卷映射，-e环境配置。</p><p>容器启动成功后在本地连接3304端口连接数据库。</p><p><u>匿名挂载：-v  容器内路径</u></p><p><code>docker run -d -p --name nginx01 -v /etc/nginx nginx</code></p><p><u>具名挂载：-v  卷名:容器内路径</u></p><p><code>docker run -d -p --name nginx01 -v nginx_01:/etc/nginx nginx</code></p><p>默认卷挂载到本地的<code>/var/lib/docker/volumes/</code></p><p><u>指定路径挂载：-v  本地路径:容器路径</u></p><p><code>docker run -d -p --name nginx01 -v /home/nginx01/:/etc/nginx nginx</code></p><p><strong>卷管理</strong></p><p><code>docker volume [options]</code>，管理数据卷，参数说明：</p><p>create ，创建一个卷；<br>inspect，查看卷详细信息；<br>ls，列出所有卷；<br>prune，删除未使用的本地卷；<br>rm，删除卷  </p><p><strong>方式二、dockerfile</strong></p><p>1、创建一个dockerfile文件</p><p><code>vim dockerfile</code></p><p>2、编写dockerfile</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">FROM centos</span><br><span class="line">VOLUME [&quot;volume01&quot;]</span><br><span class="line">CMD echo &quot;----end----&quot;</span><br><span class="line">CMD /bin/bash</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">解释</span></span><br><span class="line">FROM 镜像名:标签 #FROM指定基础镜像，其必须为Dcokerfile中第一条指令</span><br><span class="line">RUN 命令   #在该镜像中执行命令，如果执行多个命令用 &amp;&amp;连接起来</span><br></pre></td></tr></table></figure><p>3、docker build 生成镜像文件</p><p><code>docker build -f /home/dockerfile -t 镜像名(repo):版本标签(tag) .</code></p><p><strong>方式三、容器之间–volumes-from</strong></p><p>创建一个容器docker02与其他容器共享数据。</p><p><code>docker run -it --name docker02 --volumes-from docker01 nginx</code></p><p>删除容器docker01数据依旧存在，因为它的数据在本地&#x2F;var&#x2F;lib&#x2F;docker&#x2F;volume下。</p><h4 id="DockerFile"><a href="#DockerFile" class="headerlink" title="DockerFile"></a>DockerFile</h4><p>dockerfile用来构建镜像文件，命令脚本。</p><p><u>dockerfile指令</u>：</p><p>FROM  #基础镜像<br>MAINTAINER  #维护者，姓名+邮箱<br>RUN  #<u>镜像构建时需要运行的命令</u><br>ADD  #添加文件，若是压缩包则自动解压<br>WORKDIR #镜像工作目录<br>VOLUME  #挂载目录<br>EXPOSE #保留端口配置<br>CMD #指容器启动需要运行的命令，只有最后一个生效，追加命令会覆盖<br>ENTRYPOINT #指定容器运行时需要的命令，可以docker run时追加命令<br>ONBUILD #当构建一个被继承的dockerfile时触发onbuild指令<br>COPY #将主机文件拷贝到制作的镜像中<br>ENV  #构建时设置环境变量</p><p><strong>示例</strong>：</p><p>vim编辑dockerfile文件：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vim mydockerfile-centos</span><br></pre></td></tr></table></figure><p>编写一个mycentos镜像文件：</p><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">FROM</span> centos</span><br><span class="line"><span class="keyword">MAINTAINER</span> z&lt;zzh@xxx.com&gt;</span><br><span class="line"><span class="keyword">ENV</span> MYWORKPATH /usr/local</span><br><span class="line"><span class="keyword">WORKDIR</span><span class="language-bash"> <span class="variable">$MYWORKPATH</span></span></span><br><span class="line"><span class="keyword">RUN</span><span class="language-bash"> yum -y install vim</span></span><br><span class="line"><span class="keyword">RUN</span><span class="language-bash"> yum -y install net-tools </span></span><br><span class="line"><span class="keyword">EXPOSE</span> <span class="number">80</span></span><br><span class="line"><span class="keyword">CMD</span><span class="language-bash"> <span class="built_in">echo</span> <span class="variable">$MYWORKPATH</span></span></span><br><span class="line"><span class="keyword">CMD</span><span class="language-bash"> <span class="built_in">echo</span> <span class="string">&quot;---end---&quot;</span></span></span><br><span class="line"><span class="keyword">CMD</span><span class="language-bash"> /bin/bash</span></span><br></pre></td></tr></table></figure><p>生成镜像：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker build -f mydockerfile-centos -t mycentos:1.0 . </span><br></pre></td></tr></table></figure><p><strong>CMD与ENTRYPOINT的关系</strong></p><p>相同点：</p><p>1、只能写一条，如果写了多条，那么只有最后一条生效。</p><p>2、容器启动时才运行，运行时机相同。</p><p>不同点：</p><table><thead><tr><th>说法</th><th>真相</th></tr></thead><tbody><tr><td>“ENTRYPOINT不会被运行的command覆盖”</td><td>❌ 部分错误 - <code>docker run &lt;command&gt;</code> 可以覆盖 CMD，但加上 <code>--entrypoint</code> 才能覆盖 ENTRYPOINT</td></tr><tr><td>“CMD则会被覆盖”</td><td>✅ 正确 - <code>docker run &lt;command&gt;</code> 确实会覆盖 CMD</td></tr><tr><td>“同时写了ENTRYPOINT和CMD，会互相覆盖，谁在最后谁生效”</td><td>❌ 严重错误 - 它们不会互相覆盖，而是组合执行</td></tr></tbody></table><p>当 Dockerfile 中 同时存在 ENTRYPOINT 和 CMD 时，它们不会互相覆盖，而是：</p><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">ENTRYPOINT</span><span class="language-bash"> [<span class="string">&quot;python&quot;</span>, <span class="string">&quot;app.py&quot;</span>]      <span class="comment"># 固定执行的命令（入口点）</span></span></span><br><span class="line"><span class="keyword">CMD</span><span class="language-bash"> [<span class="string">&quot;--help&quot;</span>]                        <span class="comment"># 默认参数（可被覆盖）</span></span></span><br></pre></td></tr></table></figure><p>实际执行：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">docker run myimage</span><br><span class="line"><span class="comment"># 执行: python app.py --help</span></span><br><span class="line">docker run myimage --port 8080</span><br><span class="line"><span class="comment"># 执行: python app.py --port 8080  （CMD被覆盖，ENTRYPOINT保留）</span></span><br></pre></td></tr></table></figure><p>覆盖规则详解</p><table><thead><tr><th align="left">场景</th><th align="left">结果</th><th align="left">说明</th></tr></thead><tbody><tr><td align="left"><code>docker run myimage</code></td><td align="left"><code>ENTRYPOINT</code> + <code>CMD</code></td><td align="left">CMD 作为默认参数</td></tr><tr><td align="left"><code>docker run myimage arg1</code></td><td align="left"><code>ENTRYPOINT</code> + <code>arg1</code></td><td align="left">CMD 被覆盖，ENTRYPOINT保留</td></tr><tr><td align="left"><code>docker run --entrypoint bash myimage</code></td><td align="left"><code>bash</code> + <code>CMD</code></td><td align="left">ENTRYPOINT 被强制覆盖</td></tr><tr><td align="left"><code>docker run --entrypoint bash myimage -c &quot;ls&quot;</code></td><td align="left"><code>bash -c &quot;ls&quot;</code></td><td align="left">两者都被覆盖</td></tr></tbody></table><p>两种模式的对比</p><ol><li>Shell 模式（不推荐，会忽略 CMD）</li></ol><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">ENTRYPOINT</span><span class="language-bash"> python app.py      <span class="comment"># Shell形式（无数组）</span></span></span><br><span class="line"><span class="keyword">CMD</span><span class="language-bash"> --<span class="built_in">help</span>                    <span class="comment"># 会被忽略！</span></span></span><br></pre></td></tr></table></figure><p>问题：Shell形式会启动 <code>/bin/sh -c</code>，CMD 不会作为参数传递。</p><ol start="2"><li>Exec 模式（推荐）</li></ol><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">ENTRYPOINT</span><span class="language-bash"> [<span class="string">&quot;python&quot;</span>, <span class="string">&quot;app.py&quot;</span>]    <span class="comment"># Exec形式</span></span></span><br><span class="line"><span class="keyword">CMD</span><span class="language-bash"> [<span class="string">&quot;--help&quot;</span>]                     <span class="comment"># 作为参数追加</span></span></span><br></pre></td></tr></table></figure><hr><p>常见最佳实践：</p><table><thead><tr><th align="left">用法</th><th align="left">Dockerfile 示例</th><th align="left">适用场景</th></tr></thead><tbody><tr><td align="left">固定命令+可变参数</td><td align="left"><code>ENTRYPOINT [&quot;nginx&quot;]</code> + <code>CMD [&quot;-g&quot;, &quot;daemon off;&quot;]</code></td><td align="left">Web服务器</td></tr><tr><td align="left">完全可覆盖</td><td align="left"><code>ENTRYPOINT [&quot;/bin/bash&quot;]</code> + <code>CMD []</code></td><td align="left">调试容器</td></tr><tr><td align="left">强制固定</td><td align="left"><code>ENTRYPOINT [&quot;python&quot;, &quot;app.py&quot;]</code> + 不写 CMD</td><td align="left">单用途工具</td></tr><tr><td align="left">脚本入口</td><td align="left"><code>ENTRYPOINT [&quot;/entrypoint.sh&quot;]</code> + <code>CMD [&quot;default&quot;]</code></td><td align="left">初始化逻辑</td></tr></tbody></table><p>总结：ENTRYPOINT 定义容器的主要命令，CMD 提供默认参数。两者是协作关系，不是竞争关系。</p><p>错误说法：</p><ol><li>“互相覆盖” → 实际是组合执行</li><li>“谁在最后谁生效” → 只有相同指令才会覆盖（如两个 ENTRYPOINT 或两个 CMD）</li><li>忽略了 <code>--entrypoint</code> 参数 才是覆盖 ENTRYPOINT 的正确方式</li></ol><p>2、如果在Dockerfile中同时写了ENTRYPOINT和CMD，并且CMD是一个完整的指令，那么它们两个会互相覆盖，谁在最后谁生效。</p><p>3、当docker run 命令中有参数时，守护进程会忽略CMD命令。使用ENTRYPOINT指令不会忽略，并且会接收docker run 参数附加到命令行中。</p><p>4、通过<code>ENTRYPOINT [&quot;/xxx.sh&quot;] </code>指定的命令在启动时无论如何都会执行，并且可以接收到了<code>docker run</code>的参数。为了使构建的容器可以正常启动，dockerfile文件必须包含一个CMD或ENTRYPOINT指令。</p><h4 id="docker网络"><a href="#docker网络" class="headerlink" title="docker网络"></a>docker网络</h4><p>docker网络是一种虚拟网络，网络为成对的bridge桥接模式。主机可与创建的容器进行通信，但是容器之间通信需要额外的操作。docker默认给容器分配一个虚拟内部ip地址，网络为docker0。</p><p>例：<code>docker exec -it tomcat01 ping tomcat01</code></p><p>无法ping通，因为容器在docker0网络，无法通过容器名ping通。</p><p><u>ping不同的另一种原因：</u></p><p><code>OCI runtime exec failed: exec failed: unable to start container process: exec: &quot;ping&quot;: executable file not found in $PATH: unknown</code></p><p>以上报错是因为docker创建的容器相当于一个miniLinux，很多命令和软件没有安装，包括network这一网络工具都没有。下载软件包只能通过apt-get或wget和curl命令。</p><p><strong>docker network网络管理</strong></p><p><code>docker network [options]</code>，options如下：</p><p>connect，连接容器到网络</p><p>create，创建网络</p><p>inspect ，查看网络</p><p>ls ，列出所有网络</p><p>rm ，删除网络</p><p><strong>–link实现单向通信</strong></p><p>运行tomcat02容器，使其能够与tomcat01通信。原理是在容器的&#x2F;etc&#x2F;hosts文件中配置了主机名与ip地址映射。</p><p><code>docker run -d --name tomcat02 --link tomcar01 tomcat</code></p><p><strong>自定义网络</strong></p><p>自定义网络支持通过容器名通信，<u>创建一个自定义网络</u>：</p><p><code>docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 Mynet</code></p><p><strong>–net指定网络</strong></p><p>使用自定义网络创建容器，在该网络中为容器分配ip：</p><p><code>docker run -d --name tomcat02 --net Mynet tomcat</code></p><p>在该网络中的容器可以相互通信，可ping tomcat02。</p><p><strong>容器-网络连接</strong></p><p>如何让一个容器连接到另一个网络或者与另一个网络中的容器通信，可使用docker network connect，原理是将容器添加到该网络中。</p><p>docker network connect [OPTIONS] NETWORK CONTAINER</p><p>Options:<br>      –alias          为网络添加作用范围scope<br>      –driver-opt      选择网络模式driver<br>      –ip     指定一个容器ip地址<br>      –link list   添加到另一个容器的连接<br>      –link-local-ip 为容器添加一个本地ip地址</p><p><code>docker network connect Mynet tomcat02</code>，将tomcat02挂到Mynet网络。</p><p>可使用docker network inspect 查看网络中有哪些容器。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;什么是Docker&lt;/p&gt;
&lt;p&gt;Docker是内核级别的虚拟化，可以在一个物理机上可以运行很多的容器实例。服务器的性能可以被压榨到极致。Docker是基于Go语言开发的，开源项目。</summary>
    
    
    
    <category term="docker" scheme="https://goodzou.github.io/categories/docker/"/>
    
    
  </entry>
  
  <entry>
    <title>linux进阶</title>
    <link href="https://goodzou.github.io/2023/1114/"/>
    <id>https://goodzou.github.io/2023/1114/</id>
    <published>2023-11-14T01:11:52.000Z</published>
    <updated>2026-02-11T15:24:24.549Z</updated>
    
    <content type="html"><![CDATA[<p>本篇介绍linux日志管理、内核升级、数据备份与恢复的相关内容。<span id="more"></span></p><p>linux系统文件结构：</p><p>&#x2F; 根目录<br>&#x2F;bin 存放必要的命令<br>&#x2F;boot 存放内核以及启动所需的文件<br>&#x2F;dev 存放设备文件<br>&#x2F;etc 存放系统配置文件<br>&#x2F;home 普通用户的宿主目录，用户数据存放在其主目录中lib 存放必要 的运行库<br>&#x2F;mnt 存放临时的映射文件系统，通常用来挂载使用<br>&#x2F;proc 存放存储进程和系统信息<br>&#x2F;root 超级用户的主目录<br>&#x2F;sbin 存放系统管理程序<br>&#x2F;tmp 存放临时文件<br>&#x2F;usr 存放应用程序，命令程序文件、程序库、手册和其它文档。<br>&#x2F;var 系统默认日志存放目录录</p><h4 id="日志管理"><a href="#日志管理" class="headerlink" title="日志管理"></a>日志管理</h4><p>rsyslogd日志管理服务，配置文件&#x2F;etc&#x2F;rsyslog.conf，各日志文件有：</p><p>&#x2F;var&#x2F;log&#x2F;boot.log系统启动日志</p><p>&#x2F;var&#x2F;log&#x2F;cron记录定时任务</p><p>&#x2F;var&#x2F;log&#x2F;lastlog记录最后一次登录信息</p><p>&#x2F;var&#x2F;log&#x2F;message记录系统重要信息</p><p>&#x2F;var&#x2F;log&#x2F;secure记录验证授权信息</p><p>&#x2F;var&#x2F;log&#x2F;wtmp和&#x2F;var&#x2F;log&#x2F;btmp 记录登录和失败登录信息</p><p>&#x2F;var&#x2F;tuned&#x2F;ulmp&#x2F;记录当前登录用户的信息</p><h5 id="日志类型"><a href="#日志类型" class="headerlink" title="日志类型"></a>日志类型</h5><p>auth      #pam产生的日志</p><p>authpriv    #ssh\ftp等登录产生的日志</p><p>cron  #时间任务</p><p>kern  #内核</p><p>lpr  #打印</p><p>mail  #邮件</p><p>mark  #服务内部信息</p><p>user  #用户程序信息</p><p>uucp  #主机通信</p><p>local 1-7   #自定义设备</p><h5 id="日志级别"><a href="#日志级别" class="headerlink" title="日志级别"></a>日志级别</h5><p>由低到高：</p><p>debug  调试信息</p><p>info   一般信息</p><p>notice 重要信息</p><p>warning 警告</p><p>err  错误</p><p>crit 严重错误</p><p>alert 修改</p><p>emerge 内核崩溃</p><p>none  不记录</p><h4 id="日志轮替"><a href="#日志轮替" class="headerlink" title="日志轮替"></a>日志轮替</h4><p>创建新的日志文件，删除旧的日志，日志轮替配置文件&#x2F;etc&#x2F;logrotate.conf。日志轮替依赖于系统定时任务，在&#x2F;etc&#x2F;cron.daily&#x2F;目录下有logrotate可执行文件。</p><p>keep 4 weeks worth of backlogs<br>rotate 4</p><p>#共保存 4 份日志文件,当建立新的日志文件时,旧的将会被删。</p><p>use date as a suffix of the rotated file</p><p>dateext</p><p>#以日期为后缀</p><h5 id="自定义日志轮替"><a href="#自定义日志轮替" class="headerlink" title="自定义日志轮替"></a>自定义日志轮替</h5><p>自定义日志轮替规则，如：</p><p>&#x2F;var&#x2F;log&#x2F;wtmp {<br>    monthly<br>    create 0664 root utmp<br>minsize 1M<br>    rotate 1<br>}</p><p>1）直接在&#x2F;etc&#x2F;logrotate.conf中写入该日志轮替规则。</p><p>2）在&#x2F;etc&#x2F;logrotate.d&#x2F;目录中建立新的日志文件，在文件中写入轮替策略。</p><p>参数说明：</p><p>daily，每天轮替</p><p>weekly，每周轮替</p><p>monthly ， 每月轮替</p><p>rotate  数字， 保留日志文件个数</p><p>compress ，轮替时对旧的日志压缩</p><p>create mode  owner  group ， 创建新日志时指定权限所有者所在组</p><p>mail  address ，日志轮替时输出内容到指定邮件地址</p><p>missingok ， 若日志不存在则忽略该日志警告</p><p>notifepmty ，  若日志为空则不轮替</p><p>minsize  大小 ，日志大小达到最小值才会轮替</p><p>size  大小，日志只有大于指定大小才进行轮替</p><p>dateext ，使用日期作为日志文件后缀</p><p>sharedscripts， 在此关键字后脚本只执行一次</p><p>prerotate&#x2F;endscript，在日志轮替之前执行脚本</p><p>postrotate&#x2F;endscript ，在日志轮替之后执行脚本</p><h4 id="内存日志"><a href="#内存日志" class="headerlink" title="内存日志"></a>内存日志</h4><p>journalctl   查看内存日志</p><p>journalctl  -n   3，查看最近3条</p><p>journalctl  –since   1:00  –until   1:30   ，查看某时间段日志</p><p>journalctl  -p   err，查看报错日志</p><p>journalctl  -o  verbose ，查看详细信息</p><p>journalctl _PID&#x3D;xxx  _COMM&#x3D;sshd ，查看带某参数信息的日志</p><p>journalctl  | grep sshd</p><h4 id="内核升级"><a href="#内核升级" class="headerlink" title="内核升级"></a>内核升级</h4><p>将linux系统的内核升级到兼容的版本</p><p><code>uname -a</code>：查看当前内核</p><p><code>yum info kernel -q</code>：检测内核版本</p><p><code>yum update kernel</code>：升级内核</p><p><code>yum list kernel -q</code>：查看已安装的内核</p><h4 id="数据备份和恢复"><a href="#数据备份和恢复" class="headerlink" title="数据备份和恢复"></a>数据备份和恢复</h4><p>2种方式：</p><p>1）把需要的文件用tar打包</p><p>2）dump和restore指令</p><p>若没有两个指令需要安装</p><p>yum  -y  install dump</p><p>yum  -y  install restore</p><h5 id="dump"><a href="#dump" class="headerlink" title="dump"></a>dump</h5><p>分区备份，增量备份，备份上次增加或修改过的文件</p><p><code>dump -cu -[123456789] -f 备份后的文件 -T 日期 [目录或文件系统]</code></p><p>参数说明：</p><p>-c，创建新的备份文件</p><p>-[0,1,2,3,4,5,6,7,8,9]，备份的层级，0为完整备份，0以上则备份上次修改或新增的文件，9以后再次轮替。</p><p>-f，指定备份后的文件名</p><p>-j，使用bizlib库压缩</p><p>-T，指定开始备份的时间和日期</p><p>-u，备份后在&#x2F;etc&#x2F;dumpdares中记录备份的文件系统，层级、日期时间</p><p>-t，指定文件名，若文件已备份则列出名称</p><p>-W，显示需要备份的文件及最后一次备份的层级时间</p><p>-w，只显示需要备份的文件</p><p>查看备份时间文件：cat  &#x2F;etc&#x2F;dumpdates</p><p>应用案例：</p><p>案例1：将&#x2F;boot分区所有内容备份到&#x2F;opt&#x2F;boot.bak.bz2文件中，备份的层级为0</p><p><code>dump -0uj -f /opt/boot.bak.bz2 /boot/</code> </p><p>案例2：在&#x2F;boot下创建一个新的文件hello.txt，增量备份，备份层级为1</p><p><code>vi hello.txt</code></p><p><code>dump -1uj -f /opt/boot.bak1.bz2  /boot/</code></p><p><strong>注意，dump不支持文件和目录的增量备份，只能0级备份</strong></p><p>例：备份&#x2F;etc目录到&#x2F;opt&#x2F;etc.bak.bz2</p><p><code>dump -0j -f /opt/etc.bak.bz2  /etc/</code></p><h5 id="restore"><a href="#restore" class="headerlink" title="restore"></a>restore</h5><p>恢复dump备份的数据文件。</p><p>restore  [模式mode]  [选项option]</p><p>首先指定一种模式执行，<code>-C</code>对比，<code>-i</code>交互，<code>-r</code>还原，<code>-t</code>查看文件。</p><p>选项：-f  备份文件，从dump备份文件中读取数据。</p><p>案例1：比较备份文件和原文件</p><p><code>restore -C -f /opt/boot.bak.bz2</code></p><p>案例2：查看备份了那些文件</p><p><code>restore -t -f /opt/boot.bak.bz2</code></p><p>案例3：还原备份文件&#x2F;opt&#x2F;boot.bak0.bz2到&#x2F;opt&#x2F;boottmp&#x2F;下</p><p><code>mkdir /opt/boottmp</code></p><p><code>cd /opt/boottmp</code></p><p>#恢复到第一次完全备份状态</p><p><code>restore -r -f /opt/boot.bak0.bz2</code></p><p>#若有增量备份文件boot.bak1.bz2则需要进行恢复，有几个恢复几个</p><p><code>restore -r -f  /opt/boot.bak1.bz2</code></p><h4 id="可视化管理"><a href="#可视化管理" class="headerlink" title="可视化管理"></a>可视化管理</h4><p>bt宝塔可视化管理面板可以帮助运维人员提高效率，支持一键安装集群和数据库。</p><p>安装：</p><p><code>yum install -y wget &amp;&amp; wget -O install.sh  http://download.bt.cn/install/install_6.0.sh &amp;&amp; sh install.sh</code></p><p>安装成功后显示用户名和密码。</p><h4 id="权限安全"><a href="#权限安全" class="headerlink" title="权限安全"></a>权限安全</h4><p>尽量使用普通用户登录服务器，执行root命令时使用sudo操作。对于重要的系统文件可以进行锁定，<code>chattr +i /etc/passwd</code>，使自己和他人无法操作文件。</p><p>使用SUID、SGID、Sticky设置特殊权限</p><p>使用chkrootkit&#x2F;rootkit hunter工具检查系统文件是否被修改或异常，监测root脚本，下载地址chkrootkit.org。</p><p>使用Tripwire检测系统文件完整性。</p><h4 id="IO读写监控"><a href="#IO读写监控" class="headerlink" title="IO读写监控"></a>IO读写监控</h4><p>iotop可以监控磁盘读写情况，首先安装iotop，yum install iotop，iotop。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;本篇介绍linux日志管理、内核升级、数据备份与恢复的相关内容。</summary>
    
    
    
    <category term="linux" scheme="https://goodzou.github.io/categories/linux/"/>
    
    
  </entry>
  
  <entry>
    <title>找回mysql密码</title>
    <link href="https://goodzou.github.io/2023/1113/"/>
    <id>https://goodzou.github.io/2023/1113/</id>
    <published>2023-11-13T06:16:30.000Z</published>
    <updated>2026-04-20T16:03:59.980Z</updated>
    
    <content type="html"><![CDATA[<p>这篇文章记录了如何在Linux和windows下找回mysql密码。<span id="more"></span></p><h4 id="linux下找回Mysql密码"><a href="#linux下找回Mysql密码" class="headerlink" title="linux下找回Mysql密码"></a>linux下找回Mysql密码</h4><p>1、修改&#x2F;etc&#x2F;my.cnf文件</p><p>2、插入<code>skip-grant-tables</code></p><p>3、重启mysqld服务</p><p><code>service restart mysqld</code></p><p>4、进入mysql</p><p><code>mysql -u root  -p</code></p><p>5、修改mysql数据库中user表的authentication_string字段</p><p><code>use  mysql;</code></p><p><code>update user set authentication_string=password(&quot;12345678&quot;) where user=&#39;root&#39;;</code></p><p>#刷新权限</p><p><code>flush  privileges;</code></p><p>6、退出mysql，再次修改my.cnf配置文件</p><p><code>#skip-grant-tables</code></p><h4 id="windows下找回mysql密码"><a href="#windows下找回mysql密码" class="headerlink" title="windows下找回mysql密码"></a>windows下找回mysql密码</h4><p>1、停止MySQL服务</p><p>首先，我们需要停止MySQL服务。</p><p>net stop mysql</p><p>2、修改配置文件</p><p>在MySQL停止的情况下，我们需要修改MySQL的配置文件以跳过密码验证。在MySQL的安装目录中，找到名为my.ini或my.cnf的配置文件，使用文本编辑器打开该文件。在配置文件中找到[mysqld]部分，在该部分下添加以下一行代码：<br>skip-grant-tables</p><p>3、重启MySQL服务</p><p>现在，我们需要重新启动MySQL服务，并且由于我们已经修改了配置文件，MySQL将不再需要密码进行验证。以下是重启MySQL服务的步骤：</p><p>net start mysql</p><p>4、登录MySQL并修改密码</p><p>登录MySQL数据库并修改账户密码。</p><p>mysql -u root</p><p>如果使用的是其他账户，请将root替换为其他账户名。登录成功后，我们可以使用以下命令来修改密码：</p><p><code>ALTER USER &#39;root&#39;@&#39;localhost&#39; IDENTIFIED BY &#39;new_password&#39;;</code></p><p>修改密码成功后，可以退出MySQL数据库。</p><p>5、恢复配置文件</p><p>打开MySQL的安装目录，打开之前修改的配置文件（my.ini或my.cnf），删除或注释掉之前添加的一行。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;这篇文章记录了如何在Linux和windows下找回mysql密码。</summary>
    
    
    
    <category term="数据库" scheme="https://goodzou.github.io/categories/%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
    
    
  </entry>
  
  <entry>
    <title>Linux常用指令</title>
    <link href="https://goodzou.github.io/2023/1112/"/>
    <id>https://goodzou.github.io/2023/1112/</id>
    <published>2023-11-12T09:49:23.000Z</published>
    <updated>2026-04-20T15:40:24.537Z</updated>
    
    <content type="html"><![CDATA[<p>在学习linux的过程中，有一些指令经常使用，下面是学习笔记。<span id="more"></span></p><h4 id="登录注销"><a href="#登录注销" class="headerlink" title="登录注销"></a>登录注销</h4><p>su - 用户名，logout 注销用户登录（运行级别3下有效）</p><h4 id="关机重启"><a href="#关机重启" class="headerlink" title="关机重启"></a>关机重启</h4><p>shutdown -h now 立刻关机<br>shutdown -h   1  1分钟后关机，默认一分钟<br>shutdown  -r  now  现在重启<br>shutdown -c 取消关机，取消重启<br>halt关机<br>reboot重启<br>sync同步内存数据</p><h4 id="文件目录类"><a href="#文件目录类" class="headerlink" title="文件目录类"></a>文件目录类</h4><p>ls :显示当前目录下的文件，-a 所有文件,-l 列表展示信息,-h 方便浏览<br>cat xxx:安全打开，只读<br>vi xxx:用vim编辑器打开文件<br>more: 分页打开<br>less： 打开大文件<br>head -n  11 xx  打开文件开头11行，默认10行<br>tail -n 11  xxx  打开文件末尾11行 </p><p><code>tail -f xxx实时监控文件</code></p><p>history: 命令执行记录<br><code>history 10</code>最近执行的10条指令<br><code>!488</code>执行第488条指令</p><p>rm: 删除文件 -f 强制删除, -r逐层递归删除文件<br>cp: 复制文件或覆盖，-r递归复制整个文件夹 ，&#x2F;cp强制覆盖<br>mv: 移动文件、重命名<br>touch：创建空文件<br>mkdir:创建文件夹<br>rmdir:删除文件夹<br>ln: 符号连接，软连接<br><code>ln -s /root/ myroot </code>  </p><h4 id="打包、解包、压缩和解压指令"><a href="#打包、解包、压缩和解压指令" class="headerlink" title="打包、解包、压缩和解压指令"></a>打包、解包、压缩和解压指令</h4><p>Linux 主要有3种压缩方式，zip，gzip，xz是压缩解压指令。<br>gzip：压缩速度最快，应用广泛<br>bzip2：压缩成的文件小<br>xz：最新的方式，提供最佳的压缩率</p><p>tar是打包指令：</p><p>-c，进行打包<br>-v，显示详细信息<br>-f，指定压缩或解压的文件包<br>-z，支持gzip压缩和解压，-j支持bzip压缩，-J支持xz压缩<br>-x，解包文件<br>-t，显示文件的内容<br>-k，保留原有文件不覆盖<br>-C，切换到指定目录</p><p>案例1：压缩多个文件</p><p>tar -zcvf  pc.tar.gz  &#x2F;home&#x2F;pig.txt  &#x2F;home&#x2F;cat.txt  </p><p>案例2：将&#x2F;home文件夹压缩</p><p>tar  -zcvf  myhome.tar,gz  &#x2F;home&#x2F;</p><p>案例3：将myhome.tar.gz解压到当前目录</p><p>tar -zxvf myhome.tar.gz</p><p>案例4：将myhome.tar.gz解压到&#x2F;opt&#x2F;tmp2下</p><p>mkdir  &#x2F;opt&#x2F;tmp2<br>tar  -zxvf  &#x2F;home&#x2F;myhome.tar.gz  -C  &#x2F;opt&#x2F;tmp2</p><p>1、tar<br>打包：tar cvf   xxx.tar  xxx文件<br>解包：tar xvf   xxx.tar</p><p>2、.gz<br>压缩：gzip  xxx<br>解压：gunzip xxx.gz</p><p>3、.tar.gz 和 .tgz<br>压缩：tar zcvf xxx.tar.gz  xxxx<br>解压：tar zxvf xxx.tar.gz</p><p>4、.tar.bz2<br>压缩：tar jcvf xxx.tar.bz2  xxx<br>解缩：tar jxvf xxx.tar.bz2 </p><p>5、.zip<br>压缩：zip   xxx.zip  xxx<br>解压：unzip xxx.zip</p><p>例：<br>zip  -r  myhome.zip  &#x2F;home&#x2F;把home文件夹及子文件压缩<br>unzip  -d  &#x2F;opt&#x2F;tmp   myhome.zip  把myhome.zip解压到&#x2F;opt&#x2F;tmp</p><p>6、.rar<br>压缩：rar a xxx.rar xxx<br>解压：rar   x xxx.rar</p><p>7、.bz2<br>压缩： bzip2 -z xxxx<br>解压1：bzip2 -d &#x2F;xxx  xxx.bz2<br>解压2：bunzip2 xxx.bz2</p><h4 id="输出重定向-和追加-指令"><a href="#输出重定向-和追加-指令" class="headerlink" title="输出重定向&gt;和追加&gt;&gt;指令"></a>输出重定向<code>&gt;</code>和追加<code>&gt;&gt;</code>指令</h4><p>ls -l &gt; info.txt<br>cat 文件1&gt; 文件2<br>echo “xxx”&gt;文件</p><p><code>echo $PATH $HOSTNAME</code></p><h4 id="时间日期类"><a href="#时间日期类" class="headerlink" title="时间日期类"></a>时间日期类</h4><p>date: 打印当前时间</p><p><code>date &quot;+%Y-%m-%D&quot;</code>打印年月日<br><code>date set &quot;+%Y-%m-%D %H:%M:%S&quot;</code></p><p>cal :打印当前日历</p><p><code>cal 2023</code>打印2023年日历</p><h4 id="搜索查找类"><a href="#搜索查找类" class="headerlink" title="搜索查找类"></a>搜索查找类</h4><p>find :-name  ；-user； -size</p><p><code>find /home -name hello.txt -user root找到/home目录下用户名为root的hello.txt文件</code><br><code>find /  -size +200M  在根目录下找大于200M的文件</code></p><p>locate: 无需遍历整个系统，查询快</p><p><code>updatedb</code><br><code>locate hello.txt 定位hello.txt所在的目录</code></p><p>grep: 过滤查找</p><p><code>cat hello.txt | grep -n &quot;hello&quot;  </code>   打开hello.txt并查找hello所在行</p><p>案例1：在&#x2F;home目录下查找所有内容含cat的文件名</p><p><code>grep -r &quot;cat&quot; /home | cut -d &quot;:&quot; -f 1 </code></p><p>案例2：请统计&#x2F;home目录下所有文件个数及文件的总行数</p><p><code>find -r /home -name &quot;*.*&quot; | wc -l</code></p><p><code>find -r /home -name &quot;*.*&quot; |xargs wc -l</code></p><h4 id="用户管理"><a href="#用户管理" class="headerlink" title="用户管理"></a>用户管理</h4><p>添加用户：useradd xxx<br>删除用户：userdel xxx<br>修改用户所在组：usermod -g 新组名 user<br>修改用户登录目录：usermod -d 目录 user<br>查看用户是否存在：id user<br>查看当前用户：whoami</p><h4 id="文件所有者所在组"><a href="#文件所有者所在组" class="headerlink" title="文件所有者所在组"></a>文件所有者所在组</h4><p>添加&#x2F;删除组：groupadd&#x2F;groupdel<br>查看了文件的所有者：ls -ahl  xxx<br>改变文件的所有者：chown  新用户  文件名&#x2F;目录<br>改变文件所在组：chgrp  新组名 文件名&#x2F;目录<br>改变文件目录所有者所在组：chown  newowner:newgroup</p><p><strong>加上参数-R将目录下所有文件都会改变</strong> </p><h4 id="权限管理"><a href="#权限管理" class="headerlink" title="权限管理"></a>权限管理</h4><p><code>-rw-r--r--. 1 root root  239 10月 24 10:13 hello.java</code> （1）<br><code>drwxr-xr-x. 2 root root 4096 10月 23 13:56 桌面</code>               （2）<br> r:读 w:写 x:执行    [4,2,1]  r+w+x&#x3D;7</p><p><code>0~9位：</code></p><p>1）<code>0位：文件类型，-文件，d目录，c字符设备(键盘鼠标)，b块设备(硬盘)</code><br>2）<code>1~3位：所有者权限</code><br>3）<code>4~6：所在组权限</code><br>4）<code>7~9：其他组权限</code></p><p>[rwx]作用于文件<br>r:只读cat； w:写修改但不能删除 ；x:可执行<br>[rwx]作用于目录<br>r: 查看目录内容ls，但对文件有独立的权限;  w：可在目录内创建、删除文件 ; x：可进入目录cd</p><p>注（1）中数字1表示文件数或子目录数，第一个root为用户，第二个为用户组，239为文件大小单位字节，“10月 24 10:13”为最后修改日期。</p><h4 id="变更权限"><a href="#变更权限" class="headerlink" title="变更权限"></a>变更权限</h4><p>chmod修改文件目录的权限, u所有者，g所在组，o其他组，a所有人<br>(1)+ ，-， &#x3D;<br>chmod  u&#x3D;rwx,g&#x3D;rx,o&#x3D;x   文件名&#x2F;目录 ：给用户rwx，所在组rx，其他组x<br>chmod  o+x  文件名&#x2F;目录  ：给其他组执行权限<br>chmod   a-x   文件名&#x2F;目录  ：所有人剥夺执行权限</p><p>(2)通过数字变更</p><p>r：4 w：2 x：1  </p><p>rwx&#x3D;7，rw：6，rx:5，r：4，wx：3，w：2，x：1  无权限：0</p><p><code>chmod  u=rwx,g=rx,o=x  文件名/目录</code>相当于<code>chmod 751 文件名/目录</code></p><h4 id="crond任务时间调度"><a href="#crond任务时间调度" class="headerlink" title="crond任务时间调度"></a>crond任务时间调度</h4><p>设置任务调度文件：&#x2F;etc&#x2F;crontab</p><p>设置个人调度任务：执行crontab -e指令，接着输入任务到调度文件。如：</p><p> <code>*/1 * * * * ls -l /etc &gt; /tmp/to.txt</code> </p><p>每小时每分钟执行<code>ls -l /etc &gt; /tmp/to.txt</code> </p><p>五个占位符参数：</p><p>第一个*：一小时中第几分钟，0~59</p><p>第二个*：一天中第几小时，0~23</p><p>第三个*：一个月中第几天，1~31</p><p>第四个*：一年中第几个月，1~12</p><p>第五个*：一周中星期几，0~7（0,7为星期日）</p><p>crond相关指令：</p><p><code>-r</code>终止任务调度（删除），<code>-e</code>编辑任务，<code>-l</code>列出任务</p><p><code>service crond restart</code>重启crond调度</p><p>特殊符号说明：</p><p><code>*</code> ：任何时间，若第一个为*表示每分钟执行。</p><p><code>,</code>：逗号代表不连续的时间，<code>0 8,12,16 * * *</code>表示每天8点12点16点0分执行。</p><p><code>-</code>：表示连续时间范围，<code> 0 5 ** 1-6</code>周一到周六凌晨五点执行。</p><p><code>*/n</code>：表示每隔多久执行，<code>*/10 </code>表示每隔10分钟执行。</p><p>案例1：<br><u>每隔一分钟将当前时间和日期追加到&#x2F;home&#x2F;mycal中</u><br>1）vim my.sh写入<code>date &gt;&gt; /home/mycal和cal &gt;&gt; /home/mycal</code><br>2)   给my.sh增加执行权限，<code>chmod u+x my.sh</code><br>3)  crontab -e写入任务<code>*/1 * * * *  /home/my.sh</code></p><p>案例2：<br><u>每天凌晨2点备份数据库mydb</u><br>1）crontab -e<br>2）0 2 * * *  mysqldump -u root -p 123456  mydb &gt; &#x2F;home&#x2F;db.bak</p><h4 id="at定时任务"><a href="#at定时任务" class="headerlink" title="at定时任务"></a>at定时任务</h4><p>at命令是一次性定时任务，at的守护进程atd检查作业队列，以后台模式运行，每60秒检查一次，若时间与当前时间匹配则运行。使用at要保证atd在运行，使用以下命令查看进程：</p><p><code>ps -ef | grep atd</code></p><p>at [选项] [时间]</p><p>atq查询任务，atrm删除<br>Ctrl+D结束命令输入</p><p>at时间格式：<br>1）hh:mm(04:00)当天或第二天执行<br>2）midnight&#x2F;noon&#x2F;teatime<br>3）12小时制，如9am ,3pm<br>4）指定具体日期：month day或mm&#x2F;dd&#x2F;yy或dd.mm.yy，指定日期必须在时间后面04:00 2023-11-1<br>5）相对计时：<code>now  + count time-units</code>，now是当前时间，count是数量，tim-units是时间单位，minutes,hours,days,months,weeks。</p><p><code>now + 5 days</code>五天后<br><code>5pm + 2 days</code>两天后下午五点</p><p>6）today、tommorow</p><p>案例1：<br>每天下午5点，把时间输入到&#x2F;root&#x2F;data.log里</p><ol><li><code>at 5pm tommorow</code></li><li><code>date  &gt; /root/data.log</code></li></ol><p>案例2：<br>2天后五点执行&#x2F;bin&#x2F;ls &#x2F;home<br>1)<code>at 5pm + 2 days</code><br>2)<code>/bin/ls /home</code></p><p>案例3：<br>删除5号at任务<br><code>atrm 5</code></p><h4 id="磁盘分区"><a href="#磁盘分区" class="headerlink" title="磁盘分区"></a>磁盘分区</h4><p>磁盘分区和文件系统之间通过挂载mount联系起来，分区构成整个文件系统。</p><p>查看设备挂载情况：<code>lsblk  -f</code></p><p>Linux硬盘分为IDE和SCSI类型，目前主要为SCSI。</p><p>对于IDE硬盘，驱动器标识符为<code>hdx~</code>，hd表示IDE设备类型，x表示盘号(a主盘，b从属盘,c…)，<code>~</code>表示分区，前四个分区用1-4数字表示，5开始为逻辑分区。如hda2表示第一个IDE硬盘的第二个分区。</p><p>对于SCSI&#x2F;SATA硬盘，标识为“sdx~”，sd为SCSI硬盘类型，其余和IDE表示类似。</p><p>对于NVMe硬盘，以nvme0n开头，nvme0n1p1是第一块硬盘的第一个分区，而nvme0n2p3则是第二块硬盘的第三个分区。</p><p><u>案例：挂载一块硬盘</u></p><p>1.虚拟机添加硬盘</p><p>在虚拟机设置中在设备列表里添加一块硬盘，完成后重启才能识别。</p><p>2.分区</p><p><code>fdisk /dev/sdb</code></p><p>m 显示命令列表<br>p 显示磁盘分区，fdisk -l<br>n 新增分区<br>d 删除分区<br>w写入并退出<br>q 直接退出</p><p>3.格式化</p><p>格式化文件系统获得UUID：<code>mkfs -t ext4 /dev/sdb1</code></p><p>4.挂载</p><p>将分区和文件目录联系起来：mount 设备名 文件目录</p><p><code>mount /dev/sdb1  /newdisk</code></p><p>umount 设备名或挂载目录(mountpoint)</p><p><code>umount  /dev/sdb1</code>或<code>umount /newdisk</code></p><p>但是重启后失效</p><p>5.设置自动挂载</p><p>实现永久挂载，修改&#x2F;etc&#x2F;fstab文件，添加设备，重启或执行mount -a立刻生效。</p><p><code>/dev/sdb1              /newdisk                    0.0</code></p><h4 id="磁盘情况查询"><a href="#磁盘情况查询" class="headerlink" title="磁盘情况查询"></a>磁盘情况查询</h4><p>1）查看磁盘整体使用情况 ：<code>df -h</code></p><p>2）查询指定目录的磁盘占用情况</p><p><code>du -h</code> 默认当前目录</p><p>-s  占用大小汇总<br>-h 带计量单位<br>-a 带上文件<br>–max-depth&#x3D;1子目录深度<br>-c 列出明细，增加汇总值</p><p>案例1：查询&#x2F;opt目录下一层子目录的磁盘使用情况</p><p><code>du -h --max-depth=1 /opt</code></p><p>3）查看内存使用情况：<code>free -h</code></p><ol start="4"><li>查看系统负载命令：<code>uptime</code><br>-p，显示系统运行时间，-s，显示系统启动时间</li></ol><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">15:32:56 up  6:59,  2 users,  load average: 0.04, 0.03, 0.05</span><br><span class="line">15:32:56当前时间，up运行时间，2个用户，过去1、5、15分钟负载</span><br></pre></td></tr></table></figure><p>5）虚拟内存状态、io状态<br>vmstat\iostat</p><h4 id="工作实用指令"><a href="#工作实用指令" class="headerlink" title="工作实用指令"></a>工作实用指令</h4><p>1）统计&#x2F;opt文件夹下文件的个数</p><p><code>ls -l /opt | grep &quot;^-&quot; | wc -l</code> </p><p>2）统计&#x2F;opt文件夹下目录的个数</p><p><code>ls -l /opt | grep &quot;^d&quot; | wc -l</code></p><p>3）统计&#x2F;opt文件夹下文件的个数，包括子文件夹下的文件</p><p><code>ls -lR /opt | grep &quot;^-&quot; | wc -l</code> </p><p>4）统计&#x2F;opt文件夹下目录的个数，包括子文件夹下</p><p><code>ls -lR /opt | grep &quot;^d&quot; | wc -l</code> </p><p>5）树状显示目录结构</p><p><code>yum install tree</code><br><code>tree /opt</code></p><h4 id="Linux网络配置"><a href="#Linux网络配置" class="headerlink" title="Linux网络配置"></a>Linux网络配置</h4><p>查看网络配置：ifconfig或ip addr<br>测试网络连通：ping ip地址</p><p>1）DHCP动态路由分配</p><p>自动分配，ip不会冲突，但是不固定。</p><p>2）静态分配IP(固定IP)</p><p>修改网卡配置文件，<code>vi /etc/sysconfig/network-scripts/ifcfg-ens33</code>，将ip地址配置成静态的，添加固定ip如下。</p><p>DEVICE&#x3D;ens33 (设备网卡名)<br>HWADDR&#x3D;00:0c:2x:6x:0x:xx  (MAC地址)<br>TYPE&#x3D;ethernet  (网络类型)<br>UUID&#x3D;029ad-d12e-dw23-32da-1d2c (随机id)<br>ONBOOT&#x3D; yes  (系统启动时网络接口是否开启)<br>BOOTPROTO&#x3D;static  (ip配置方法)[none;static;dhcp;bootp]<br>IPADDR&#x3D;192.168.157.129 (ip地址)<br>GATEWAY&#x3D;192.168.157.2  (网关)<br>DNS1&#x3D;192.168.157.2   (DNS解析器)</p><p>在虚拟机中找到虚拟网络编辑器，找到该网络如vmnet8，修改子网ip地址为192.168.157.0，修改NAT配置中网关地址，点击确定再点击应用。之后重启系统或者输入<code>service network restart</code>生效。</p><h4 id="设置主机名和HOST映射"><a href="#设置主机名和HOST映射" class="headerlink" title="设置主机名和HOST映射"></a>设置主机名和HOST映射</h4><p>查看主机名：hostname<br>修改主机名：<code>vi /etc/hostname</code>，重启</p><p>设置HOST映射：</p><p>1）在windows的”C:\Windows\System32\drivers\etc\hosts”下添加ip与主机名的映射：192.168.157.129    CentOS7</p><p>ping CentOS7可ping通。<br>windows cmd窗口<code>ipconfig /displaydns</code>列出本地dns缓存，<code>ipconfig /flushdns</code>清除缓存。</p><p>2）linux在&#x2F;etc&#x2F;hosts文件修改</p><h4 id="监控网络状态"><a href="#监控网络状态" class="headerlink" title="监控网络状态"></a>监控网络状态</h4><p>netstat  [options]，选项说明：</p><p>-an，按一定顺序输出  ；-p，显示哪个进程在调用</p><p>例如：查看sshd服务的信息</p><p><code>netstat -anp |grep sshd</code></p><p>ping  ip，监测网络连接。</p><p>案例：统计连接到服务器的各个ip情况，并按连接数从大到小排</p><p><code>netstat -an | grep ESTABLISHED | awk  -F  &quot; &quot;   &#39;&#123;print $5&#125;&#39;  | cut  -d  &quot;:&quot;  -f  1 | sort |uniq -c | sort -nr</code> </p><p>案例：使用tcpdump监听本机来自192.168.300.1，端口为22的通信，并保存到tcpdump.log</p><p><code>tcpdump -i ens33 host 192.168.300.1 and port 22 &gt;&gt; /opt/tcpdump.log</code></p><h4 id="进程管理"><a href="#进程管理" class="headerlink" title="进程管理"></a>进程管理</h4><p>1、查看进程信息</p><p>ps指令：</p><p>ps -A 显示当前所有进程信息<br>ps -a 显示所有终端机下执行的程序<br>ps -u 以用户的格式显示进程信息<br>ps  -x 显示后台进程运行的参数<br>ps -aux | grep sshd 查看也没有sshd服务</p><p>USER进程执行用户，PID进程号，%CPU进程占CPU百分比，%MEM进程占物理内存的百分比，VSZ进程占虚拟内存的大小，RSS进程占物理内存的大小KB，TTY终端名称，STAT进程状态(S睡眠，R正在运行，Z僵尸进程，D短期等待，T被跟踪或被停止，&gt;高优先级)，START进程开始执行时间，TIME占用CPU时间，COMMAND启动进程的命令或参数。</p><p>ps -ef 以全格式显示所以进程，不会截断，System V格式。</p><p><code>ps -ef | grep sshd</code></p><p>UID用户ID，PID进程号，PPID父进程PID，TTY终端设备号。</p><p>&#x2F;&#x2F;查找进程状态信息<br>cat &#x2F;proc&#x2F;pid&#x2F;status<br>&#x2F;&#x2F;查找进程启动命令行<br>cat &#x2F;proc&#x2F;pid&#x2F;cmdline</p><p>2、终止进程</p><p>kill  [option]  PID  ，终止进程，option&#x3D;-9表示强制终止。<br>killall 进程名  ，终止该进程及子进程。</p><p><u>案例1：踢掉某个非法登录用户tom</u><br>root       1143      1  0 16:23 ?        00:00:00 &#x2F;usr&#x2F;sbin&#x2F;sshd -D<br>root       1741   1143  0 16:23 ?        00:00:00 sshd: root@pts&#x2F;0<br>root       5031   1143  1 20:03 ?        00:00:00 sshd: tom [priv]<br>tom        5043   5031  0 20:03 ?        00:00:00 sshd: tom@pts&#x2F;1</p><p><code>kill  5031</code></p><p>案例2：终止远程登录服务器sshd，合适时重启服务</p><p><code>kill 1143</code><br><code>/bin/systemctl start sshd.service</code></p><p>案例3：终止多个gedit</p><p><code>killall gedit</code></p><p>案例4：强制杀死终端</p><p>root       5277   5267  0 20:16 pts&#x2F;1    00:00:00 bash</p><p><code>kill -9 5277</code></p><p>3、查看进程树</p><p>pstree ，-p 显示进程号，-u显示用户</p><p>4、<u>top动态监控进程（性能监视器，任务管理器）</u></p><p>top: 实时显示进程信息<br>top -d 秒数，几秒后刷新<br>top -i，不显示闲置和僵尸进程<br>top -p PID，指定某进程ID监控进程</p><p>top交互操作说明，输入：</p><p>P，以CPU使用率排序，默认<br>N，以PID排序<br>M，以内存使用率排序<br>q，退出<br>u，接着输入用户名，回车即可查看某用户相关进程<br>k，接着输入进程号，回车即可终止某进程</p><p>案例1：监控用户tom的相关进程</p><p>top，输入u，接着输入tom</p><p>案例2：终止指定进程，踢掉tom</p><p>PID   USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM   TIME+ COMMAND<br>2060  tom       20   0    160988   2372   1028 S   0.0    0.1     0:00.01     sshd<br>2064  tom       20   0    116876   3296   1676 S   0.0     0.2    0:00.04    bash </p><p>top，输入u回车，输入tom回车，输入k回车，输入2064回车。</p><h4 id="Service服务管理"><a href="#Service服务管理" class="headerlink" title="Service服务管理"></a>Service服务管理</h4><p>查看系统服务：</p><p>1）setup，系统服务。<br>2）service指令管理服务在&#x2F;etc&#x2F;init.d查看，<code>ls -l /etc/init.d/</code></p><p><u>service指令：</u></p><p>service  服务名  [start|stop|restart|reload|status]</p><p>CentOS7以后，大多服务不使用service，而使用systemctl。<br>[使用service关闭network服务，一旦关闭，将无法连接SSH服务]</p><p>服务运行级别，在&#x2F;etc&#x2F;initab查看，<code>systemctl get-default</code>查看当前运行级别，<code>systemctl set-default T.target</code>设置默认级别。</p><p><u>chkconfig指令：</u></p><p>查看服务：chkconfig –list<br>设置服务在level级别开启：<code>chkconfig  --level  [0/1/2/3/4/5/6]  服务名  on/off</code></p><p>如设置network在3级别打开或关闭：<code>chkconfig --level 3 network on/off</code>，之后重启系统即可生效。</p><p><u>systemctl指令：</u></p><p>systemctl   [start |stop |restart |status |enable |disable]  服务名</p><p>该指令管理的服务在&#x2F;usr&#x2F;lib&#x2F;systemd&#x2F;system查看。</p><p>systemctl  list-unit-files，查看服务开机启动状态<br>systemctl  enable  服务名 ，设置服务开机启动<br>systemctl  disable  服务名，关闭服务自启动<br>systemctl   is-enabled  服务名 ，查看某个服务是否自启动</p><p>案例1：查看当前防火墙状态，关闭和打开防火墙服务</p><p><code>systemctl  list-unit-files  | grep  firewalld</code>  或<br><code>systemctl is-enabled firewalld</code>  或<br><code>systemctl  status firewalld</code><br><code>systemctl  stop firewalld</code><br><code>systemctl  start firewalld</code></p><p>【systemctl start&#x2F;stop 服务，服务在系统重启后恢复原样】</p><p>案例2：防火墙打开和关闭指定端口111</p><p>打开打开：<code>firewall-cmd --permanent  --add-port=端口/协议</code><br>关闭打开：<code>firewall-cmd  --permanent  --remove-port=端口/协议</code><br>重载生效：<code>firewall-cmd  --reload</code><br>查询端口是否开放：<code>firewall-cmd  --query-port=端口/协议</code><br>查看那些已添加端口：<code>firewall-cmd --list-port</code></p><p><code>firewall-cmd --permanent  --add-port=111/tcp</code><br><code>firewall-cmd --reload</code></p><h4 id="RPM和YUM"><a href="#RPM和YUM" class="headerlink" title="RPM和YUM"></a>RPM和YUM</h4><p>rpm</p><p>rpm是linux常用的下载和打包安装工具，生成.rpm结尾的文件。</p><p>软件包名基本格式：firefox-68.10.0-1.el7.centos.x86_64，名称：firefox，版本：68.10.0，适用操作系统：el7.centos.x86_64，norach表示32位64位通用。<br>1）查询<br>查询已安装的rpm包列表：rpm -qa | grep xxx<br>查询所有rpm包：rpm -qa<br>查询软件包是否安装：rpm  -q  软件包名<br>查询软件包信息：rpm -qi  软件包名<br>查询软件包中的安装文件：rpm -ql  软件包名<br>查询文件所属软件包：rpm -qf  文件全路径<br>例：rpm -qf &#x2F;etc&#x2F;passwd<br>2）安装<br>安装软件包：rpm  -ivh   软件包全路径，-i安装，-h显示进度，-v显示安装信息<br>例：rpm -ivh &#x2F;opt&#x2F;firefox-68.10.0-1.el7.centos.x86_64<br>3）卸载<br>卸载安装包：rpm  -e  xx软件包名 [erase]<br>强制卸载：rpm -e –nodeps  xxx</p><p>yum</p><p>yum是一个Shell前端软件包管理器，基于rpm，能够从服务器中下载rpm包并安装，可以自动处理依赖关系，并一次性安装所以依赖，但需要连接网络。</p><p>-y:代替手动输入yes，自动执行安装<br>查询服务器是否有可安装的xx软件：yum  list | grep xxx<br>查看可更新的软件：yum list updates<br>查看已安装的软件：yum list installed<br>安装包：yum install   xxx<br>删除软件：yum remove  xxx<br>更新所以软件：yum update<br>更新一个软件：yum update xxx<br>检查更新的包：yum check-update<br>清除缓存目录下的软件包：yum clean all<br>更新软件包缓存：yum makecache<br>搜寻软件包：yum  search  xxx<br>查看软件包信息：yum info xxx<br>设置镜像的仓库：<br><code>yum config-manager  --add-repo  http://mirrors.aliyun.com/repo/Centos-7.repo</code></p><p>影响yum使用的主要文件:<br>基本设置： &#x2F;etc&#x2F;yum.conf<br>仓库软件源集： &#x2F;etc&#x2F;yum.repo.d&#x2F;*.repo<br>日志文件： &#x2F;var&#x2F;log&#x2F;yum.log</p><p>*.repo的基本配置项：App-Stream BaseOS Extras<br>[base]：名称，可以随意取<br>name：描述repo含义<br>baseurl：后面接地址，是指定一个固定地址<br>enabled:是否启用此频道（1为启动，0为不启用）<br>gpgcheck：是否需要查阅RPM文件内数字证书<br>gpgkey：数字证书公钥文件所在位置，使用默认值</p><p>开启软件缓存 nano  &#x2F;etc&#x2F;yum.conf</p><p>案例：使用yum安装firefox<br>rpm -e firefox<br>yum list | grep firefox<br>yum install  firefox</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;在学习linux的过程中，有一些指令经常使用，下面是学习笔记。</summary>
    
    
    
    <category term="linux" scheme="https://goodzou.github.io/categories/linux/"/>
    
    
  </entry>
  
  <entry>
    <title>Shell编程</title>
    <link href="https://goodzou.github.io/2023/1111/"/>
    <id>https://goodzou.github.io/2023/1111/</id>
    <published>2023-11-11T04:58:45.000Z</published>
    <updated>2026-02-11T15:24:54.884Z</updated>
    
    <content type="html"><![CDATA[<p>Shell是一个命令解释器，它提供一个向内核发送请求的界面系统级程序。<span id="more"></span></p><h5 id="1、shell入门"><a href="#1、shell入门" class="headerlink" title="1、shell入门"></a>1、shell入门</h5><p>脚本格式要求：以#!&#x2F;bin&#x2F;bash开头，脚本要有可执行权限。</p><p>编写一个shell脚本：</p><p>vim  hello.sh<br><code>#!/bin/bash</code><br><code>echo &quot;hello java~&quot;</code></p><p>脚本执行方式：</p><p>1）输入脚本的绝对或相对路径，前提要有执行权限</p><p>.&#x2F;hello.sh</p><p>2）sh +脚本，没有权限也能执行</p><p>sh  .&#x2F;hello.sh</p><h5 id="2、变量"><a href="#2、变量" class="headerlink" title="2、变量"></a>2、变量</h5><p>shell变量分为系统变量和自定义变量，变量名称可以由字母、数字、下划线组成，字母一般大写。</p><p>系统变量：<code>$PATH $HOME $PWD $USER $SHELL</code><br>显示当前所有变量：set<br>定义变量：变量名&#x3D;值（等号不要空格）<br>撤销变量：unset 变量<br>声明静态变量：readonly  变量（不可以unset）</p><p><code>#!/bin/bash</code><br><code>A=10</code><br><code>echo &quot;A=$A&quot;</code></p><p>将命令的返回值赋值给变量：A&#x3D;`date`或A&#x3D;$(date)</p><p><u>设置环境变量:</u></p><p>export  变量名&#x3D;变量值  （将shell变量输出为环境变量\全局变量）<br>source  配置文件<br>echo  $变量名</p><p>案例：在&#x2F;etc&#x2F;profile中定义TOMCAT_HOME环境变量，查看环境变量的值，在另一个shell程序中使用<code>TOMCAT_HOME</code></p><p>在profile文件中写入：export TOMCAT_HOME&#x3D;&#x2F;opt&#x2F;tomcat<br>更新环境变量：source  &#x2F;etc&#x2F;profile</p><p><u>位置参数变量：</u></p><p>在执行脚本时可以传入相应的参数，如：.&#x2F;myshell.sh  100 200</p><p>基本语法：</p><p><code>$&#123;&#125;</code>：获取变量的值，{}可省略；<br><code>$n</code>：(n代表数字，<code>$0</code>为命令本身，<code>$1-9</code>代表第一到第九个参数，10以上用大括号<code>$&#123;10&#125;</code>)；<br><code>$@</code>：代表所有参数，但区分每个参数；<br><code>$#</code> ：命令行中参数个数；<br><code>$*</code> ：代表所有参数，所有参数看为一个整体</p><p><u>预定义变量：</u></p><p>$$:当前进程的进程号<br>$!：后台运行的最后一个进程的进程号<br>$?：最后一次执行命令的返回状态，0代表执行成功，非0失败</p><p><code>#!/bin/bash</code><br><code>echo   &quot;当前执行进程的id=$$&quot;</code></p><p>#以后台运行一个脚本，并获取它的进程号。</p><p>.&#x2F;myshell.sh 100 200  &amp;<br><code>echo  &quot;最后一个执行进程id=$!&quot;</code><br><code>echo  &quot;执行结果=$?&quot;</code></p><h5 id="3、shell注释"><a href="#3、shell注释" class="headerlink" title="3、shell注释"></a>3、shell注释</h5><p>单行注释：#<br>多行注释：</p><p>:&lt;&lt;!<br>内容<br>!</p><h5 id="4、运算符"><a href="#4、运算符" class="headerlink" title="4、运算符"></a>4、运算符</h5><p>（1）$$((运算式))</p><p>（2）$[运算式]</p><p>（3）expr表达式（运算符之间要有空格）</p><p>+、-、\*、&#x2F;、%加减乘除取余</p><p>expr   5 + 3<br><strong>注意空格</strong></p><p>案例1：计算(2+3)x4<br>第一种方式<br><code>res=$(((2+3)*4))</code><br>或第二种<br><code>res=$[(2+3)*4]</code><br>或expr<br>temp&#x3D;`expr  2 + 3`</p><p>res&#x3D;`$temp  \*  4`</p><p>echo  “$res” </p><h5 id="5、条件判断"><a href="#5、条件判断" class="headerlink" title="5、条件判断"></a>5、条件判断</h5><p>(1)if语句</p><p>单分支：</p><p>if [ 条件 ]<br>then<br>代码<br>fi</p><p>两个分支：</p><p>if [ 条件 ]<br>then<br>代码<br>else<br>代码<br>fi</p><p>多分支：</p><p>if [ 条件 ]<br>then<br>代码<br>elif [ 条件 ]<br>then<br>代码<br>else<br>代码<br>fi</p><p>(2)条件语句： [ 条件 ]  注意左右空格</p><p>例：</p><p>[ abc ]  返回true<br>[  ]  返回false<br>[ 条件 ]  &amp;&amp; echo ok ||  echo !ok  条件满足执行后面语句</p><p>判断条件：</p><p>1)&#x3D;字符串比较</p><p>2)整数比较</p><p>-lt 小于（&lt;）<br>-le 小于等于(&lt;&#x3D;)<br>-eq 等于（&#x3D;&#x3D;）<br>-gt  大于(&gt;)<br>-ge 大于等于(&gt;&#x3D;)<br>-ne 不等于(!&#x3D;)</p><p>3）按照文件权限判断</p><p>-r   有读权限<br>-w  有写权限<br>-x  有执行权限</p><p>4）按照文件类型判断</p><p>-f  文件存在&amp;普通文件<br>-e  文件存在<br>-d 文件存在&amp;为目录</p><p>案例：编写一个shell程序，输入一个数，大于大于60及格，小于不及格</p><p><code>#!/bin/bash</code><br><code>if [ $1  -ge  60 ]</code><br><code>then</code><br><code>echo &quot;及格&quot;</code><br><code>elif [ $1 -lt 60 ]</code><br><code>then</code><br><code>echo &quot;不及格&quot;</code><br><code>fi</code></p><h5 id="6、case语句"><a href="#6、case语句" class="headerlink" title="6、case语句"></a>6、case语句</h5><p>case expression in</p><pre><code>pattern1)    statement1    ;;pattern2)    statement2    ;;pattern3)    statement3    ;;#其他pattern情形*)    statement</code></pre><p>esac</p><p><u>pattern支持正则表达式</u>：</p><p>*  ：表示任意字符串。<br>[abc]：表示 a、b、c 三个字符中的任意一个。比如，[15ZH] 表示 1、5、Z、H 四个字符中的任意一个。<br>[m-n]：表示从 m 到 n 的任意一个字符。比如，[0-9] 表示任意一个数字，[0-9a-zA-Z] 表示字母或数字。<br>|：表示多重选择，类似逻辑运算中的或运算。比如，abc | xyz 表示匹配字符串 “abc” 或者 “xyz”。</p><p>expression 既可以是一个变量、一个数字、一个字符串，还可以是一个计算表达式，或者是命令的执行结果，只要能够得到 expression 的值就可以。pattern 可以是一个数字、一个字符串，甚至是一个简单的正则表达式。有时候相近的内容作为一个pattern时，这些内容以“|”隔开，比如 y|yes|YES)。如果 expression 和某个模式匹配成功，就会执行这模式后面对应的所有语句（该语句可以有一条，也可以有多条），直到遇见双分号;;才停止，然后整个 case 语句就执行完了，程序会跳出整个 case 语句，执行 esac 后面的其它语句。如果 expression 没有匹配到任何一个模式，那么就执行*)后面的语句，直到遇见双分号;;或者esac才结束。最后一个分支可以写;;，也可以不写。执行到 esac 都会结束整个 case in 语句。最后一个分支*)并不是什么语法规定，它只是一个正则表达式，*表示任意字符串。</p><p>case  $1  in<br>    1)<br>       echo “星期一”<br>        ;;<br>    2)<br>        echo “星期二”<br>        ;;<br>    3)<br>        echo “星期三”<br>        ;;<br>    #其他pattern情形<br>    *)<br>        echo “others”<br>esac</p><h5 id="7、循环"><a href="#7、循环" class="headerlink" title="7、循环"></a>7、循环</h5><h6 id="for循环"><a href="#for循环" class="headerlink" title="for循环"></a>for循环</h6><p>案例一：编写一个num.sh，将输入的数字参数输出</p><p><code>#!/bin/bash</code><br><code>for  i   in   &quot;$@&quot;</code><br><code>do</code><br><code>echo  &quot;n  is $i&quot;</code><br><code>done</code></p><p>案例二：编写一个sum.sh对1到100求和输出结果</p><p><code>#!/bin/bash</code><br><code>SUM=0</code><br><code>for(( i=1;i&lt;=100;i++ ))</code><br><code>do</code><br>​<code>$SUM=$[$SUM+Si]</code><br><code>done</code><br><code>echo  $SUM</code></p><h6 id="while循环"><a href="#while循环" class="headerlink" title="while循环"></a>while循环</h6><p>while  [ 条件 ]<br>do<br>代码<br>done</p><p>案例：从命令行输入一个参数n，计算1+2+…+n</p><p>#!&#x2F;bin&#x2F;bash<br><code>SUM=0</code><br><code>i=1</code><br><code>while  [ $i  -le  $1 ]</code><br><code>do</code><br>​<code>SUM=$[ $SUM+$i ]</code><br>​<code>i=$[ $i+1 ]</code><br><code>done</code><br><code>echo  $SUM</code></p><h5 id="8、read读取输入"><a href="#8、read读取输入" class="headerlink" title="8、read读取输入"></a>8、read读取输入</h5><p>-p  :  读取时的提示；-t：读取等待的秒数</p><p>案例：读取控制台输入的两个数输出</p><p><code>read  -p  &quot;请输入一个数=&quot;  NUM1</code><br><code>echo   $NUM1</code><br><code>read   -t  10  -p  &quot;请在10秒内输入一个数=&quot;  NUM2</code><br><code>echo   $NUM2</code></p><h5 id="9、函数"><a href="#9、函数" class="headerlink" title="9、函数"></a>9、函数</h5><p><u>系统函数basename\dirname</u><br>basename返回路径后的文件名，dirname返回文件所在文件夹绝对路径</p><p>案例1：取出&#x2F;home&#x2F;myhome&#x2F;a.txt的a.txt文件名</p><p><code>basename  /home/myhome/a.txt</code>  </p><p>案例2：取出&#x2F;home&#x2F;myhome&#x2F;a.txt的文件的绝对路径&#x2F;home&#x2F;myhome</p><p><code>dirname  /home/myhome/a.txt</code></p><p>自定义函数:<br>function  函数名(){</p><p>}</p><p>调用函数：函数名+参数值</p><p>案例：定义一个函数getSum()求出输入参数之和</p><p>#!&#x2F;bin&#x2F;bash<br><code>function  getSum()&#123;</code><br>​<code>SUM=$[$n1+$n2]</code><br>​<code>echo &quot;$SUM&quot;</code><br><code>&#125;</code><br><code>read -p  &quot;请输入一个数n1=&quot;   n1</code><br><code>read -p  &quot;请输入一个数n2=&quot;   n2</code><br><code>getSum   $n1  $n2</code></p><h5 id="10、备份数据库练习"><a href="#10、备份数据库练习" class="headerlink" title="10、备份数据库练习"></a>10、备份数据库练习</h5><p>在&#x2F;usr&#x2F;sbin下创建数据库备份脚本文件mysql_db_backup.sh</p><p>#!&#x2F;bin&#x2F;bash<br><code>BACKUP=/data/backup/db</code><br>#时间<br><code>DATETIME=$(date  +%Y-%m-%d_%H%M%S)</code><br><code>echo  $DATETIME</code><br>#主机<br><code>HOST=localhost</code><br>#用户<br><code>DB_USER=root</code><br>#密码<br><code>DB_PASS=root</code><br>#数据库名<br><code>DATABASE=MyDB</code><br>#创建备份目录<br><code>[ ! -d &quot;$&#123;BACKUP&#125;/$&#123;DATETIME&#125;&quot; ] &amp;&amp; mkdir -p  &quot;$&#123;BACKUP&#125;/$&#123;DATETIME&#125;&quot;</code><br>#备份<br><code>mysqldump  -u&#123;DB_USER&#125;  -p&#123;DB_PASS&#125;  --host=$&#123;HOST&#125;  -q -R  --datebases  $&#123;DATABASE&#125;  |  gzip &gt;  $&#123;BACKUP&#125;/$&#123;DATETIME&#125;/$DATETIME.sql.gz</code><br>#打包<br><code>cd  $&#123;BACKUP&#125;</code><br><code>tar -zcvf  $DATETIME.tar.gz  $&#123;DATETIME&#125;</code><br>#删除备份目录<br><code>rm -rf  $&#123;BACKUP&#125;/$&#123;DATETIME&#125;</code><br>#删除10天前备份数据<br><code>find  $&#123;BACKUP&#125; -atime 10+ -name &quot;\*.tar.gz&quot; -exec rm -rf &#123;&#125; \</code><br><code>echo  &quot;备份$DATABASE 成功~&quot;</code></p><p>每天2点半定时备份<br>crontab -e<br><code>30 2 * * *  /usr/sbin/mysql_db_backup.sh</code></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;Shell是一个命令解释器，它提供一个向内核发送请求的界面系统级程序。</summary>
    
    
    
    <category term="linux" scheme="https://goodzou.github.io/categories/linux/"/>
    
    
  </entry>
  
  <entry>
    <title>虚拟机IP突然消失的解决方法</title>
    <link href="https://goodzou.github.io/2023/1102/"/>
    <id>https://goodzou.github.io/2023/1102/</id>
    <published>2023-11-02T01:19:39.000Z</published>
    <updated>2026-02-11T15:27:00.063Z</updated>
    
    <content type="html"><![CDATA[<p>基础方法如下：<br>如果这个文件 ifcfg-ens33已经将ONBOOT改为yes了，之前用的还好好的还有ip地址的但是现在没有了。<span id="more"></span></p><p>1.先重启网络服务</p><p><code>sudo service network restart</code><br>但是重启网络失败的话，那就执行下边命令。</p><p>出现问题<br>解决方式：禁用NetworkManager</p><p>2.执行下边这两条命令</p><p><code>systemctl stop NetworkManager</code><br><code>systemctl disable NetworkManager</code></p><p>3.执行完成之后重新启动网络服务。</p><p><code>sudo service network restart</code></p><p>然后重新启动网络服务就OK了</p><p>4.输入ip addr查看ip地址。<br>在使用VMware过程中，有时候出现ssh工具连接不上的情况，在VMware中使用ip addr或者ifconfig命令查看ip，发现ip已消失</p><p>若为kali系统，可以试试以下命令：<br><code>dhclient eth0</code><br>eth0为网卡名字，该操作为网卡自动分配ip。</p><p>解决办法二：<br>1、查看VMware的网络设置，网络适配器，将网络适配器设置为NAT模式<br>2、设置VMware的虚拟网络编辑器，设置NAT模式的网段。（点击虚拟网络编辑器”更改设置“。如果不点击这里，“还原默认设置”按钮将是灰色的）<br>3、点击虚拟网络编辑器“还原默认设置”。</p><p>解决办法三：<br>重启网卡：<br><code>service network restart</code><br>如果报错，则可以停止网络管理，<code>systemctl stop NetworkManager</code>。若为kali则重启网络服务为<code>service networking restart</code></p><p>解决办法四：<br>由于启动服务器时未加载网卡，导致IP地址初始化失败。修改网络初始化配置，设定网卡在系统启动时初始化。<br><code>cat /etc/sysconfig/network-scripts/ifcfg-ens33</code></p><p>进入目录：&#x2F;etc&#x2F;sysconfig&#x2F;network-scripts</p><p>编辑文件：vi ifcfg-ens33，将ONBOOT&#x3D;no改为ONBOOT&#x3D;yes</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;基础方法如下：&lt;br&gt;如果这个文件 ifcfg-ens33已经将ONBOOT改为yes了，之前用的还好好的还有ip地址的但是现在没有了。</summary>
    
    
    
    <category term="虚拟机" scheme="https://goodzou.github.io/categories/%E8%99%9A%E6%8B%9F%E6%9C%BA/"/>
    
    
  </entry>
  
  <entry>
    <title>linux找回root密码，CentOS7</title>
    <link href="https://goodzou.github.io/2023/1027/"/>
    <id>https://goodzou.github.io/2023/1027/</id>
    <published>2023-10-27T04:55:09.000Z</published>
    <updated>2026-02-11T15:24:31.446Z</updated>
    
    <content type="html"><![CDATA[<p>linux找回密码需要进入单用户模式，linux运行级别有0~6，可以通过init切换。<span id="more"></span></p><p>0：关机</p><p>1：单用户模式</p><p>2：多用户状态无网络</p><p>3：多用户状态有网络muti-user.target</p><p>4：系统未使用保留用户</p><p>5：图形用户graphical.target</p><p>6：系统重启</p><p>进入单用户模式通过以下步骤找回密码：</p><p>1.进入系统，在开机界面按住’e’进入编辑界面</p><p>2.进入编辑界面，找到’Linux16’开头的行，在末尾输入: init&#x3D; &#x2F;bin&#x2F;sh</p><p>3.按住快捷键ctrl+x进入单用户模式</p><p>4.在光标闪烁的位置输入：mount -o  remount,rw &#x2F; ，完成后enter回车</p><p>5.在新的一行最后输入passwd回车，输入密码即可</p><p>6.接着输入touch  &#x2F;.autorelabel回车</p><p>7.然后输入exec  &#x2F;sbin&#x2F;init回车，等待系统完成重启</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;linux找回密码需要进入单用户模式，linux运行级别有0~6，可以通过init切换。</summary>
    
    
    
    <category term="linux" scheme="https://goodzou.github.io/categories/linux/"/>
    
    
  </entry>
  
  <entry>
    <title>写在大学最后的时光</title>
    <link href="https://goodzou.github.io/2023/0208/"/>
    <id>https://goodzou.github.io/2023/0208/</id>
    <published>2023-02-08T08:49:24.000Z</published>
    <updated>2026-02-11T15:15:38.831Z</updated>
    
    <content type="html"><![CDATA[<p>当决定考研的那一刻，我并不知道我为什么一定要考研，内心的我是这样想的：考上了研究生，将来会更有优势。想法非常的单纯，在将近一年的准备的过程中，感觉身心疲惫，简直就是在折磨自己，每天三点一线的模式，大部分时间就是坐在板凳上看书学习，只能靠着听音乐和以后所谓的“美好将来”的幻想来迷惑自己<span id="more"></span>。</p><p>到了考试前终于停下了，整个人一度的摆烂，考试那几天几乎没有看书，那些天有特殊情况，学校让隔离了，本来大家都要回家隔离再回来考试的，全宿舍都被感染了新冠，考试当天就发烧头痛。已经无所谓了，就这两三天，也没什么心思看书，只希望早点回家。当坐在回去的列车上，看着窗外飞速而过的树木，我知道这一年真的太不容易。</p><p>就像22年的卡塔尔世界杯，梅西带领阿根廷闯进世界杯决赛，我在拼命的刷数学题，我们每个人都在拼命地朝着目标前进，都在付出，最后阿根廷赢了，而我并不知道自己赢没赢，人生真的是一场比赛或者游戏吗？</p><p>回想大学四年的时光，自己懵懵懂懂地进入大学，把以前的无知和天真带了进来，左右折腾也没有找到那些大牛口中声称的“热爱”，可能是校园太小了吧，或者是我的能力和资源有限的原因吧，我迷茫地花掉了大学的大部分时光，期间也曾起来试图摆脱平庸，和其他人一起考了大学英语四六级，但是考过了又怎样。进入大三了，我又开始陷入另一种迷茫，要不要考研？网上各种声音，说考研是人生的一个转折点；说考研是一种逃避毕业工作的途径；又说考研是为了将来更好的自己，还说是在提高自己，我不懂在提高自己的什么，血压和体重吗？还是自己的受骗率？只是为了学历就算了，说实话，我还是挺佩服那些努力考上研的同学，网上基本上都是知名大学，每当看到我们这些双非院校的能逆袭985、211的视频，我更加迷茫了，我不知道是为了什么，我只是照着别人的路线继续按部就班地走下去罢了，从来没有听听内心的真实想法。所以一年下来也非常痛苦，搬书来来回回，晚上10点多才回宿舍，总是在逃避着什么。我也不清楚，进入大学以来一个人突然地像被释放了，自由独立多了，没有什么约束，也就迷茫了。从来没有人向我介绍大学生活，也没有人告诉我们大学里应该怎么做，网上倒是有很多教程，有关生活的各个方面，都被安排的明明白白。</p><p>马上就要毕业了，我依然是这样的不知所措，准备毕设加可能的研究生复试加就业，我不可能面面俱到，人生就是这样，有得有失，我准备好了面对结果了。也许我并不在乎最终结果，大概只是喜欢努力过后能有一点收获的感觉吧，可能有付出并不会有回报，就挺打击的。</p><p>如果我如愿考上研究生，那就针对的起大学这几年的颓废和迷茫吗？我又陷入了一种“迷茫”，我在读研究生，每天看着各种文献，敲着代码，导师可能一个星期就联系一次，等到毕业了又不知道如何面对以后的生活，如何融入这个陌生的社会，如何在这个世界生存下去，只能说学校建了一堵厚厚的墙，把里面的我们保护的很好，看起来我只是换了一个学校继续迷茫罢了。当初大学填报志愿的时候只是怀着一丝好奇选择了计算机专业，大学之前也没有太多机会接触计算机，直到枯燥的课程扑面而来，我才算是入门计算机了，之后的话几乎停留在理论的基础上，运用的最多是和同学一起玩游戏，大学期间电脑游戏也算是入门了。</p><p>可能是时间的原因，推着我们向前走，直到认清自己，开始接受现实，我只是一个“被编好程序的人”，安排来接受教育之后进入社会打工罢了，不然怎么叫程序员呢。当程序员也好呀，走到了人生的最后几步了，不用担心没有钱吃喝住了，心里踏实平静多了，也许不迷茫了，当然买房可能是个问题，不过比待业在家啃老强，好歹有个稳定收入养活一家子。以后操心事多了，就不继续遐想了，总之顺其自然吧，父母希望子女有个出息，但是我也就这样了，他们知不知道我们现在的压力呢，年轻人拼了命的努力，最后只为了在目标城市买到一块安生之地。</p><p>虽然现在考试分数还没出来，但是毕设和企业春招已经开始了，是时候走出迷茫了，走一个令人舒畅的路了，疫情这几年真不容易，真是难受！</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;当决定考研的那一刻，我并不知道我为什么一定要考研，内心的我是这样想的：考上了研究生，将来会更有优势。想法非常的单纯，在将近一年的准备的过程中，感觉身心疲惫，简直就是在折磨自己，每天三点一线的模式，大部分时间就是坐在板凳上看书学习，只能靠着听音乐和以后所谓的“美好将来”的幻想来迷惑自己</summary>
    
    
    
    <category term="碎碎念" scheme="https://goodzou.github.io/categories/%E7%A2%8E%E7%A2%8E%E5%BF%B5/"/>
    
    
  </entry>
  
  <entry>
    <title>关于28岁的字节工程师离世的感慨</title>
    <link href="https://goodzou.github.io/2022/0224/"/>
    <id>https://goodzou.github.io/2022/0224/</id>
    <published>2022-02-24T08:36:49.000Z</published>
    <updated>2026-02-11T15:26:24.728Z</updated>
    
    <content type="html"><![CDATA[<p>此前网传年轻的字节跳动员工吴同学已离世，字节跳动的内网一则通知显示，从医院获知，吴同学不幸离世。经过大概是这样的，2月21日晚上18点，28岁的吴同学走进字节跳动的健身房，吴同学在运动约1小时后出现头晕现象，情况加重。当晚19时30分许，急救人员到场救治，并将其送上救护车<span id="more"></span>。2月23日0时30分，医院反馈，该员工情况危重。不久，吴同学便去世了。</p><p>说到这，我是软件的本科生，起初同样是怀着兴趣报考了计算机相关的专业，以至于来到了之后不太满意的学校。28岁可以称他为‘学长’了，大了我几岁，但还是年轻的生命啊！吴学长有着名校的学历，很早就有了从事IT行业的想法，在毕业后进入字节努力地工作，结了婚，买了房，出了书，几个月前妻子还有了孩子。很难想到这样一位年轻的学长，本应在日后获得更加幸福美好的生活，然而现实却是如此的残酷，他是匆匆地离开世间。</p><p>从吴学长生前的个人平台和当前报道来看，吴学长是一位有梦想、活泼开朗的人，而且能力还是很强的，还买了房，虽然是贷款的。尽管在医院奋力的抢救，还是没能挽回吴学长的生命。吴学长的逝世给了吴学长妻子很大的压力，她打算退了房子回到小县城，和她母亲孕育孩子，抚养成人。</p><p>关于以上的消息，我对吴学长的离开感到难过和一点惋惜，惋惜他是一位值得被生活善待的人，结局令人悲痛；还对软件专业的自己感到迷茫，因为面临着就业和考研的烦恼，现在怀疑要不要继续学习计算机或进入相关的行业；也对计算机相关专业在读的同学或即将从事计算机相关行业的人感到一丝忧虑和庆幸，庆幸我们还有当前健康的身体和生活，庆幸我们还有一些选择。</p><p>吴学长的离世不是孤例，另一位26岁的年轻人也上了热搜。上海一家设计事务所的设计师，疑似因加班过度，于15日清晨在出租屋内猝死。两个年轻人的离世消息撞到一起，给人的冲击格外大，难道生活的真相就是如此吗？大家都知道IT行业普遍加班，工作压力很大，生活压力也大，而且相关从业者都生活在大城市。据了解，字节这样的公司喜欢自律能力强的人，基本上福利多，有免费三餐和酒水，免费健身房，五险一金等等，在这样的环境中，人不想向上爬都难，回到家也是不断地学习。</p><p>总之，IT行业就一个字，“卷”！因为社会的某种普遍共识，行业薪资高，不光是入职找工作卷，高考和研究生报考也开始卷起来了，考啥一定考计算机相关的专业，甚至跨考，光想着以后会待遇高，可是也看到了报考人数上涨，招生扩招都无法满足大家的录取，只能越来越卷。不是其他行业不卷，是计算机卷得太厉害了，炸天了！不管适不适合读相关专业，做不做的了相关工作，都涌入大潮之中，特别是人工智能开始发展兴起之后。</p><p>与此同时，在当前像字节这样的大公司里工作，可以自由地享受各种优厚待遇，也会感受到了一种被限制的自由。尽管有弹性的工作制，工作的员工要完成各种任务，和优秀的人在一起会更优秀，但是也要抗住辛苦和劳累。不是说大家不要努力向上，而是看看给自己带来了什么，忽视了长远的利益发展和被迫劳累所带来的危害，即使是强壮的巨人最终也会倒下。</p><p>当时看到吴学长的新闻，就十分感慨，他为何遭遇这样的不幸。因为很难说清他的猝死和加班有没有关联，但应该反思一下，如何在如此的生活压力下，让巨头字节和它的员工的心都保持跳动。·</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;此前网传年轻的字节跳动员工吴同学已离世，字节跳动的内网一则通知显示，从医院获知，吴同学不幸离世。经过大概是这样的，2月21日晚上18点，28岁的吴同学走进字节跳动的健身房，吴同学在运动约1小时后出现头晕现象，情况加重。当晚19时30分许，急救人员到场救治，并将其送上救护车</summary>
    
    
    
    <category term="碎碎念" scheme="https://goodzou.github.io/categories/%E7%A2%8E%E7%A2%8E%E5%BF%B5/"/>
    
    
  </entry>
  
  <entry>
    <title>ACM、OI、IOI编程竞赛模式介绍</title>
    <link href="https://goodzou.github.io/2021/1123/"/>
    <id>https://goodzou.github.io/2021/1123/</id>
    <published>2021-11-23T12:07:51.000Z</published>
    <updated>2026-02-11T15:21:53.382Z</updated>
    
    <content type="html"><![CDATA[<h5 id="介绍一下编程比赛："><a href="#介绍一下编程比赛：" class="headerlink" title="介绍一下编程比赛："></a>介绍一下编程比赛：</h5><p><strong>ICPC</strong>是国际大学生程序设计竞赛(ACM International Collegiate ProgrammingContest（ACM-ICPC或ICPC）是由美国计算机协会(ACM)主办的，一项旨在展示大学生创新能力、团队精神和在压力下编写程序、分析和解决问题能力的年度竞赛。经过近30多年的发展，ACM国际大学生程序设计竞赛已经发展成为最具影响力的大学生计算机竞赛。 </p><span id="more"></span><p><strong>CCPC</strong>中国大学生程序设计竞赛(China Collegiate Programming Contest) 是由中国大学生程序设计竞赛协会主办的面向世界大学生的国际性年度赛事，旨在激励当代大学生运用计算机编程技术和技能来解决实际问题，激发其学习算法和程序设计的兴趣，培养其团队合作意识、创新能力和挑战精神。</p><p><strong>IOI</strong>是国际信息学奥林匹克竞赛 (International Olympiad in Informatics），是面向中学生的一年一度的信息学科竞赛。举办历史较长，第一届国际信息学奥林匹克竞赛于1989年在保加利亚举行。</p><p><strong>NOI</strong>是中国计算机学会举办了全国青少年计算机程序设计竞赛，即全国青少年信息学奥林匹克竞赛(简称NOI）。</p><p><strong>NOIP</strong>是全国青少年信息学奥林匹克联赛（National Olympiad in Informatics in Provinces）自1995年至今已举办19次，每年由中国计算机学会统一组织。</p><h5 id="介绍一下编程比赛中最常见的三种赛制：ACM赛制、OI赛制、IOI赛制。"><a href="#介绍一下编程比赛中最常见的三种赛制：ACM赛制、OI赛制、IOI赛制。" class="headerlink" title="介绍一下编程比赛中最常见的三种赛制：ACM赛制、OI赛制、IOI赛制。"></a>介绍一下编程比赛中最常见的三种赛制：ACM赛制、OI赛制、IOI赛制。</h5><p>先普及一些常见的竞赛术语：提交反馈，实时排名，按点给分，题数罚时，测试点(测试用例)等。</p><ul><li><p>提交反馈：一般为“通过AC”、“答案错误”、“运行超时”等等反馈</p></li><li><p>实时排名：比赛中能看到排名，如ACM赛制</p></li><li><p>全有或全无：只有通过全部测试点才算通过，只要有一个测试点不通过就不能AC(答案accept)</p></li><li><p>按点给分：每道题有多个测试点，通过几个测试点就给相应分值</p></li><li><p>题数罚时：题数多者排名靠前，只有当题数相同时才比较时间，时间少者排名靠前，或再比较罚时，罚时为提交错误答案给予相应的罚时，罚时和比赛时间没有关系，只用来排名，每做错一道题会有罚时相加，罚时少排名靠前。</p></li></ul><p>先看通过的题目数量，单独绿色表示通过，然后看罚时，罚时短的靠前，每个题目下面黑色数字表示做出这个题的时间，然后把ac的题的时间数累加到罚时中，其间每一次提交运行结果被判错误的话将被加罚20分钟时间，未正确解答的试题不记时。红色- 表示没通过的题和错误提交次数，蓝色的表示比赛结束后的通过。</p><table><thead><tr><th>赛制</th><th>提交反馈</th><th>实时排名</th><th>计分方式</th><th>排名方式</th><th>相关比赛</th></tr></thead><tbody><tr><td>ACM制</td><td>有</td><td>有</td><td>全有或全无</td><td>题数+时间</td><td>ICPC、CCPC、牛客小白赛、传智杯</td></tr><tr><td>OI制</td><td>无</td><td>无</td><td>按点给分</td><td>分数</td><td>NOI、计算机能力挑战赛、蓝桥杯</td></tr><tr><td>IOI制</td><td>有</td><td>有</td><td>按点给分</td><td>分数</td><td>PAT、团体程序设计天梯赛、CCF</td></tr></tbody></table><p>ACM赛制：每道题提交之后都有反馈，但看不到错误的测试样例(leetcode周赛可以看到)，每道题都有多个测试点，每道题必须通过了所有的测试点才算通过。每道题不限制提交次数，没通过的话会有罚时，比赛过程中可以看到实时排名，通过题数相同的情况下按照答题时间+罚时来排名。</p><p>ACM赛制的比赛：ICPC、CCPC、codeforces、leetcode周赛及全国编程大赛、传智杯</p><p>OI赛制：每道题提交之后都没有反馈，根据每道题通过的测试点的数量获得相应的分数。每道题不限制提交次数，如果提交错误没有任何惩罚，仅以最后一次提交为准，赛后按照总得分来排名。</p><p>OI赛制的比赛：NOI、考研机试、蓝桥杯、计算机能力挑战赛。</p><p>IOI赛制：每道题提交之后都有反馈，可以实时看到自己每道题得了多少分，但看不到错误的测试样例。每道题都有多个测试点，根据通过的测试点的数量获得相应的分数。每道题不限制提交次数，如果提交错误没有任何惩罚。比赛过程中一般可以看到实时排名，按照总得分来排名。IOI赛制是结合了OI赛制和ACM赛制的特点。</p><p>IOI赛制的比赛：PAT、团体程序设计天梯赛、CCF、CSP、洛谷月赛。</p><h5 id="注意Tips："><a href="#注意Tips：" class="headerlink" title="注意Tips："></a>注意Tips：</h5><p>OI赛制和IOI赛制没有提交限制，提交错误也没有惩罚，所以可以大胆地提交，但ACM赛制的罚时会很大拉开差距。</p>]]></content>
    
    
    <summary type="html">&lt;h5 id=&quot;介绍一下编程比赛：&quot;&gt;&lt;a href=&quot;#介绍一下编程比赛：&quot; class=&quot;headerlink&quot; title=&quot;介绍一下编程比赛：&quot;&gt;&lt;/a&gt;介绍一下编程比赛：&lt;/h5&gt;&lt;p&gt;&lt;strong&gt;ICPC&lt;/strong&gt;是国际大学生程序设计竞赛(ACM International Collegiate ProgrammingContest（ACM-ICPC或ICPC）是由美国计算机协会(ACM)主办的，一项旨在展示大学生创新能力、团队精神和在压力下编写程序、分析和解决问题能力的年度竞赛。经过近30多年的发展，ACM国际大学生程序设计竞赛已经发展成为最具影响力的大学生计算机竞赛。 &lt;/p&gt;</summary>
    
    
    
    <category term="碎碎念" scheme="https://goodzou.github.io/categories/%E7%A2%8E%E7%A2%8E%E5%BF%B5/"/>
    
    
  </entry>
  
  <entry>
    <title>CC知识共享许可协议简介</title>
    <link href="https://goodzou.github.io/2021/0916/"/>
    <id>https://goodzou.github.io/2021/0916/</id>
    <published>2021-09-16T13:07:56.000Z</published>
    <updated>2026-02-11T15:22:11.743Z</updated>
    
    <content type="html"><![CDATA[<p>在博客文章下面一般都有版权声明，标明文章作者、链接和版权类型，最常见的类型就是<code>CC BY-SA</code>。这种版权声明是知识共享许可协议<code>Creative Commons license</code>，又叫CC协议或创作公共协议，是一种允许他人分发作品的公共版权许可。2002年美国非盈利性组织知识共享<code>Creative Commons</code>首次发布了CC协议，它并不是什么法律法规，而是基于一定的知识产权法律基础上建立的一种知识作品等分享约定协议，旨在促进创意作品流通和分享。</p><p>在经历了三个不同版本之后，<code>CC4.0</code>版本于2013年发布。自此之后，<code>CC4.0</code>被鼓励在全球范围内适用。除了放弃版权将作品完全公布到公共领域 (即<code>CC0</code>协议) 之外，<code>CC4.0</code>版本中一共有6种常用的版权规定组合<span id="more"></span>。下面这六种不同的许可协议，这里从基础许可到受限许可开始列举：</p><h4 id="CC-BY"><a href="#CC-BY" class="headerlink" title="CC BY"></a>CC BY</h4><p> <code>CC BY</code> : 此类型许可允许进行版本分发，作品合成修改和建立在任何媒介或格式的材料上，只要归属于创造者。许可证允许商业使用。包括以下：</p><p> BY  – 版权必须归属于作者，必须按照作者或授权人所指定的方式，保留其姓名标示。</p><h4 id="CC-BY-SA"><a href="#CC-BY-SA" class="headerlink" title="CC  BY-SA"></a>CC  BY-SA</h4><p> <code>CC BY-SA</code> : 此类型允许进行版本分发，作品合成修改和建立在任何媒介或格式的材料上，只要归属于创造者。许可证允许商业使用。假如你合成修改或者建立生成新作品，必须在相同的条款下许可授权，不能变更许可协议 。CC BY-SA 包括以下条件:</p><p> BY – 版权必须归属于作者，必须按照作者或授权人所指定的方式，保留其姓名标示。<br> SA – 修改必须在相同的条款协议下共享(Shared)</p><h4 id="CC-BY-NC"><a href="#CC-BY-NC" class="headerlink" title="CC BY-NC"></a>CC BY-NC</h4><p> <code>CC BY-NC</code> : 本许可证允许使用者仅出于非商业目的，以任何媒体或格式分发、合成、改编和构建材料作品，并且版权要归属创作者。CC BY-NC 包括以下条件:</p><p> BY  – 版权必须归属于作者，必须按照作者或授权人所指定的方式，保留其姓名标示。<br> NC  – 只能用于非商业用途(NonCommercial)</p><h4 id="CC-BY-NC-SA"><a href="#CC-BY-NC-SA" class="headerlink" title="CC BY-NC-SA"></a>CC BY-NC-SA</h4><p> <code>CC BY-NC-SA</code> : 本许可证允许使用在仅出于非商业目的下，以任何媒体或格式分发、合成、改编和构建材料作品，并且版权要归属创作者。要合成修改或者建立生成新作品，必须在相同的条款协议下许可。CC BY-NC-SA 包括以下条件:</p><p> BY  – 版权必须归属于作者，必须按照作者或授权人所指定的方式，保留其姓名标示。<br> NC – 只能被用于非商业用途<br> SA – 修改必须在相同的条款协议下共享(Shared)</p><h4 id="CC-BY-ND"><a href="#CC-BY-ND" class="headerlink" title="CC BY-ND"></a>CC BY-ND</h4><p> <code>CC BY-ND</code> : 此类许可允许仅以未修改的形式，以任何媒介或格式复制和分发作品材料，版权归属作者，但可以用于商业用途。<code>CC BY-ND</code> 包括以下:</p><p> BY  – 版权必须归属于作者，必须按照作者或授权人所指定的方式，保留其姓名标示。<br> ND  – 作品的衍生和改编是不允许的(No derivatives or adaptations )</p><h4 id="CC-BY-NC-ND"><a href="#CC-BY-NC-ND" class="headerlink" title="CC BY-NC-ND"></a>CC BY-NC-ND</h4><p> <code>CC BY-NC-ND</code> : 此类许可允许仅以未修改的形式在仅出于非商业目的下，以任何媒介或格式复制和分发作品材料, 最后版权依然归于作者。CC BY-NC-ND 包括以下:</p><p> BY  – 版权必须归属于作者，必须按照作者或授权人所指定的方式，保留其姓名标示。<br> NC  – 只能被用于非商业用途<br> ND  – 作品的衍生和改编是不允许的</p><h4 id="知识共享公共领域公开CC0"><a href="#知识共享公共领域公开CC0" class="headerlink" title="知识共享公共领域公开CC0"></a>知识共享公共领域公开CC0</h4><p> <code>CC0</code> (又名CC Zero)是一个知识作品公布共享协议，它表示创作者放弃他们的版权，将他们的作品放到全球公共领域，<code>CC0</code>允许使用者无条件地以任何媒介或格式分发、合成、改编和构建材料，是一种彻底的开放版权协议，建议谨慎使用。</p><p>附录：参考<a href="https://creativecommons.org/about/cclicenses/">CC官网</a></p>]]></content>
    
    
    <summary type="html">博客文章版权声明，知识共享许可协议CC协议</summary>
    
    
    
    <category term="碎碎念" scheme="https://goodzou.github.io/categories/%E7%A2%8E%E7%A2%8E%E5%BF%B5/"/>
    
    
  </entry>
  
  <entry>
    <title>Java类加载篇</title>
    <link href="https://goodzou.github.io/2020/1018/"/>
    <id>https://goodzou.github.io/2020/1018/</id>
    <published>2020-10-18T09:19:51.000Z</published>
    <updated>2026-02-11T15:23:46.487Z</updated>
    
    <content type="html"><![CDATA[<h4 id="Java类加载"><a href="#Java类加载" class="headerlink" title="Java类加载"></a>Java类加载</h4><p>这部分知识比较深入底层，将重点介绍类加载和反射，会提到JDK动态代理、<code>AOP</code>，反射等诸多知识点。当调用Java命令允许程序时，该命令会启动多个线程，它们都处于该Java虚拟机进程里。所有线程、变量处于同一个进程里，它们都使用JVM进程的内存区。当出现以下情况，进程将终止：<span id="more"></span></p><ul><li>程序结束</li><li>使用<code>System.exit()</code>或<code>Runtime.getRuntime().exit()</code>代码</li><li>未捕获到异常</li><li>强制结束进程</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">A</span>&#123;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="variable">a</span> <span class="operator">=</span> <span class="number">6</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><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="keyword">class</span> <span class="title class_">Test1</span>&#123;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span>&#123;</span><br><span class="line"><span class="type">A</span> <span class="variable">a</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">A</span>();</span><br><span class="line">a.a++;</span><br><span class="line">System.out.println(a.a);</span><br><span class="line">&#125;<span class="comment">//输出7</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Test2</span>&#123;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span>&#123;</span><br><span class="line"><span class="type">A</span> <span class="variable">b</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">A</span>();</span><br><span class="line">System.out.println(b.a);</span><br><span class="line">&#125;<span class="comment">//输出6</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上述代码表明，不同的进程之间是不会共享资源的，运行<code>Test1</code>和<code>Test2</code>是运行两次进程，所以第二次依然重新初始化A类。</p><h5 id="类的加载"><a href="#类的加载" class="headerlink" title="类的加载"></a>类的加载</h5><p>当需要使用某个类时，系统会通过加载、连接、初始化三个步骤完成对<code>类的初始化</code>。<code>类加载</code>指的是类加载器将类的<code>class</code>文件读入内存，并为之创建一个<code>java.lang.Class</code>对象。类加载器通常由JVM提供，也称<code>系统类加载器</code>，除此之外，开发者可以通过继承<code>ClassLoader</code>基类来创建类加载器。使用不同类加载器，可以从不同来源加载类的数据通常有以下来源：</p><ul><li>本地加载<code>class</code>文件</li><li>JAR包中加载<code>class</code>文件</li><li>网络加载</li><li>把一个Java源文件动态编译并加载</li></ul><h5 id="类的连接"><a href="#类的连接" class="headerlink" title="类的连接"></a>类的连接</h5><p>创建了<code>Class</code>对象后，系统将二进制数据合并到JRE中，这一过程可分为三个阶段：</p><ol><li>验证：验证加载的类是否有正确的内部结构</li><li>准备：为类变量分配内存，并设置默认值</li><li>解析：将类的二进制数据中的符号引用替换成直接引用</li></ol><h5 id="类初始化"><a href="#类初始化" class="headerlink" title="类初始化"></a>类初始化</h5><p>在初始化阶段，JVM负责对类进行初始化，主要对<code>类变量</code>进行初始化。初始化有两种方式：</p><ul><li>声明变量时就指定初始值</li><li>使用静态代码块指定初始值</li></ul><p>JVM初始化一个类是按照一定规则进行的，如下：</p><ul><li>如果没有加载和连接，则先加载并连接该类</li><li>如果父类没有初始化，则优先初始化其父类</li><li>如果有初始化语句，则系统先执行初始化语句</li></ul><p>所以JVM总是最先初始化<code>java.lang.Object</code>类，并顺着继承链依次加载并初始化类。程序通过以下6种方式来使用某个类和接口时，系统就会初始化该类或接口：</p><ul><li>创建类实例。包括：使用<code>new</code>关键字；反射创建；反序列化创建。</li><li>调用类方法。</li><li>访问类变量或赋值。</li><li>反射强制创建某个类或接口对应的<code>java.lang.Class</code>对象。(如果还未初始化该类)</li><li>初始化某类的子类。</li><li>使用<code>java.exe</code>命令运行某个类。</li></ul><p>对于<code>final</code>型的类变量，如果该类变量的值在编译时就确定下来，那么这个类变量相当于“宏变量”。因此使用静态类变量也不会导致该类初始化，相当于使用常量。如果<code>final</code>修饰的变量不能确定下来，必须等到运行时确定，则将使该类初始化。</p><h4 id="类加载器"><a href="#类加载器" class="headerlink" title="类加载器"></a>类加载器</h4><p>类加载器负责将<code>.class</code>文件加载到内存中，并生成一个<code>java.lang.Class</code>实例，一旦一个类被加载到JVM中，同一个类就不会被再次载入。所谓<code>同一个类</code>指的是有唯一标识的类，唯一标识是用全限定类名作为载入标识。在JVM中一个类用其全限定类名和其类加载器作为其全限定类名，例如：(类、package、加载器实例)。</p><p>当JVM启动时，会形成由三个类加载器组成的<code>初始类加载器层次结构</code>，加载顺序如下。</p><ul><li>Bootstrap ClassLoader：根类加载器(不是Java实现，一般无法访问)</li><li>Extension ClassLoader：扩展类加载器</li><li>System ClassLoader：系统类加载器</li></ul><p><code>Bootstrap ClassLoader</code>也叫引导(启动)类加载器，负责加载Java核心类。它不是<code>java.lang.ClassLoader</code>的子类，而是由<code>JVM</code>自己实现。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BootstrapTest</span>&#123;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span>&#123;</span><br><span class="line">URLS[] u = sun.misc.Launcher.getBootstrapClassPath().getURLS();</span><br><span class="line">        <span class="comment">//遍历输出根类加载器的全部URL</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i&lt;u.length;i++)&#123;</span><br><span class="line">        System.out.println(u[i].toExternalForm());</span><br><span class="line">        &#125;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>Extension ClassLoader</code>，<code>ExtClassLoader(sun.misc.Launcher$ExtClassLoader)</code>的实例，负责加载JRE的扩展目录中的JAR包的类，通过这种方式可以为Java扩展核心类以外的功能，只要把开发的类打包成JAR包即可，然后放入<code>JAVA_HOME/jre/lib/ext</code>路径即可。</p><p><code>System ClassLoader</code>，应用类加载器<code>AppClassLoader</code>的实例，它负责在JVM启动时加载来自命令<code>-classpath</code>或<code>CLASSPATH</code>环境变量所指定的JAR包和类路径。可以通过调用<code>ClassLoader</code>的<code>getSystemClassLoader()</code>方法来获取系统类加载器。</p><p>系统类加载器是当前路径，扩展类加载器的加载路径是<code>JAVA_HOME/jre/lib/ext</code>，所以可以说其父加载器为<code>null</code>也不为过，<code>getParent()</code>方法返回<code>null</code>，但根加载器可以作为其父加载器。</p><h4 id="类加载机制"><a href="#类加载机制" class="headerlink" title="类加载机制"></a>类加载机制</h4><h5 id="加载机制有三种："><a href="#加载机制有三种：" class="headerlink" title="加载机制有三种："></a>加载机制有三种：</h5><ul><li>全盘委托：当某加载器加载一个类时，该类所引用和依赖的其他类也将由该类加载器负责加载。</li><li>父类(双亲)委托：先让父类加载器加载<code>class</code>，如果无法加载则从自己的类路径中加载。</li><li>缓存机制：保证所有已加载过的类都被缓存，当需要使用某个类时，加载器先从缓存区加载该类，如不存在才会加载对应的二进制数据，并将其转换成<code>Class</code>对象存入内存。(所以修改某个类后需重启JVM才会生效)</li></ul><p>一个类加载器查找<code>class</code>和<code>resource</code>时，是通过“委托模式”进行的，它首先判断这个class是不是已经加载成功，如果没有的话它并不是自己进行查找，而是先通过父加载器，然后递归下去，直到<code>Bootstrap ClassLoader</code>，如果<code>Bootstrap ClassLoader</code>找到了，直接返回；如果没有找到，则一级一级返回，最后到达自身去查找这些对象。这种机制就叫做双亲委托。 </p><h5 id="JDK搜索类的方式"><a href="#JDK搜索类的方式" class="headerlink" title="JDK搜索类的方式"></a>JDK搜索类的方式</h5><p><code>ClassLoader</code>使用的是<code>双亲委托</code>模型来搜索类的，每个<code>ClassLoader</code>实例都有一个父类加载器的引用(不是所谓的&#x2F;&#x2F;继承的关系，是一个包含的关系），虚拟机内置的类加载器<code>Bootstrap ClassLoader</code>本身没有父类加载器，但可以用作其它ClassLoader实例的的父类加载器，但是访问<code>ExtClassLoader</code>的父加载器返回<code>null</code>。</p><p>当一个<code>ClassLoader</code>实例需要加载某个类时，它会试图在亲自搜索某个类之前，先把这个任务委托给它的父类加载器，这个过程是由上至下依次检查的，首先由最顶层的类加载器<code>Bootstrap ClassLoader</code>试图加载，如果没加载到，则把任务转交给<code>Extension ClassLoader</code>试图加载，如果也没加载到，则转交给<code>AppClassLoader</code>进行加载，如果它也没有加载得到的话，则返回给委托的发起者，由它到指定的文件系统或网络等URL中加载该类。如果它们都没有加载到这个类时，则抛出<code>ClassNotFoundException</code>异常。否则将这个找到的类生成一个类的定义，并将它加载到内存当中，最后返回这个类在内存中的实例对象。</p><p>好处：因为这样可以避免重复加载，当父亲已经加载了该类的时候，就没有必要让子ClassLoader再加载一次。考虑到安全因素，我们试想一下，如果不使用这种委托模式，那我们就可以随时使用自定义的String来动态替代java核心API中定义的类型，这样会存在非常大的安全隐患，而双亲委托的方式，就可以避免这种情况，因为String已经在启动时就被引导类加载器(Bootstrap ClassLoader)加载，所以用户自定义的<code>ClassLoader</code>永远也无法加载一个自己写的String类，除非你改变JDK中ClassLoader搜索类的默认算法。</p><h5 id="类加载步骤"><a href="#类加载步骤" class="headerlink" title="类加载步骤"></a>类加载步骤</h5><ol><li>检查是否有载入过。</li><li>如果父类加载器不存在，则使用根类加载器来载入类并返回对应<code>java.lang.Class</code>对象，否则执行第5步；如果存在则使用父类加载器去加载类并返回Class对象，不成功则执行第3步。</li><li>当前加载器从与它相关的类路径中寻找，找到就执行第4步，否则执行第5步。</li><li>从文件中载入Class，成功后同样返回<code>Class</code>对象。</li><li>抛出<code>ClassNotFoundException</code>异常。</li></ol><p>JVM规范中规定如果用户自定义的类加载器将父类加载器强制设置为<code>null</code>，那么会自动将启动类加载器设置为当前用户自定义类加载器的父类加载器。同时，即使用户自定义类加载器不指定父类加载器，那么同样可以加载到&#x2F;lib下的类。自定义类加载器不指定父类加载器是默认系统类加载器为父类加载器，按照双亲委派加载。若强制父类加载器为<code>null</code>，则其他加载器就可能不被加载。</p><p>加载流程为：系统类加载器–&gt;扩展类加载器–&gt;启动类加载器，强制设置<code>parent</code>为<code>null</code>时关系就已经断了 源代码中走<code>findBootstrapClassOrNull(name)</code>加载</p><h4 id="创建自定义类"><a href="#创建自定义类" class="headerlink" title="创建自定义类"></a>创建自定义类</h4><p>JVM中除根加载器外，其他都是<code>ClassLoader</code>抽象类的子类实例，开发者可以扩展其子类并重写所含方法来实现自定义类加载器。类加载器有两个关键方法：</p><ul><li><code>loadClass(String name,boolean resolve)</code>：类加载器的入口点，根据指定名称加载类，并获取<code>Class</code>对象。</li><li><code>findClass(String name)</code>：根据指定名称查找类。</li></ul><p>如果需要实现自定义类加载器，则可以通过重写以上两个方法来实现。<code>loadClass()</code>方法的执行步骤如下：</p><ul><li>用<code>findLoadedClass(String)</code>来检查是否已经加载类，已加载则返回。</li><li>在父类加载器上调用<code>loadClass()</code>方法。如果父类为<code>null</code>，则使用根类加载器来加载。</li><li>调用<code>findClass(String)</code>方法查找。</li></ul><p>重写<code>finsClass()</code>方法可以避免覆盖默认类加载器的<code>双亲委托</code>、缓存机制，在<code>ClassLoader</code>里还有一个核心方法<code>Class defineClass(String name,byte[] b,int off,int len)</code>，负责将类的字节码文件读入字节数组<code>byte[] b</code>内，并把它转化为<code>Class</code>对象。<code>defineClass()</code>方法管理JVM的许多复杂的实现，不能被重写，因为是<code>final</code>的。除此外还有一些普通方法如下：</p><ul><li><code>findSystemClass(String name)</code>：从本地文件系统装入文件，存在就用<code>defineClass()</code>方法将字节码转换成<code>Class</code>对象。</li><li><code>static getSystemClassLoader()</code>：返回系统类加载器</li><li><code>getParent()</code>：获取父类加载器。</li><li><code>resolveClass(Class&lt;?&gt; c)</code>：链接指定类。</li><li><code>findLoaderClass(String name)</code>:如果已加载该类，则返回<code>Class</code>实例，否则返回<code>null</code>。</li></ul><h5 id="使用自定义类的好处"><a href="#使用自定义类的好处" class="headerlink" title="使用自定义类的好处"></a>使用自定义类的好处</h5><p>它能用来实现以下功能：</p><ul><li>执行代码前自动验证数字签名</li><li>根据提供的密码解密代码，避免反编译</li><li>动态加载类</li><li>把数据以字节码形式加载到应用中</li></ul><h4 id="URLClassLoader实现类"><a href="#URLClassLoader实现类" class="headerlink" title="URLClassLoader实现类"></a>URLClassLoader实现类</h4><p>该类是<code>SystemClassLoader</code>和<code>ExtClassLoader</code>的父类，注意不同上面的所谓“父类”，这里是类之间的继承关系。有如下两个构造器：</p><ul><li><code>URLClassLoader(URL[] urls)</code>：使用默认父类加载器创建一个<code>ClassLoder</code>对象，并从指定路径加载类。</li><li><code>URLClassLoader(URL[] urls,ClassLoader parent)</code>：不同上面的构造器，它可以指定一个父类加载器加载类。</li></ul><p>获得<code>URLClassLoader</code>对象后，就可以调用<code>loadClass()</code>方法来加载指定类。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> Connection coon;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> Connection <span class="title function_">getConn</span><span class="params">(String url,String user,String pass)</span> <span class="keyword">throws</span> Exception&#123;</span><br><span class="line"><span class="keyword">if</span>(conn = <span class="literal">null</span>)&#123;</span><br><span class="line">URL[] urls = &#123;<span class="keyword">new</span> <span class="title class_">URL</span>(<span class="string">&quot;file:mysql-connector-java-5.1.30-bin.jar&quot;</span>)&#125;;</span><br><span class="line"><span class="type">URLClassLoader</span> <span class="variable">myClassLoader</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">URLClassLoader</span>(urls);</span><br><span class="line">        <span class="comment">//加载MySQL的JDBC驱动，并创建一个实例</span></span><br><span class="line"><span class="type">Driver</span> <span class="variable">driver</span> <span class="operator">=</span> (Driver)myClassLoader.loaderClass(<span class="string">&quot;com.mysql.jdbc.Driver&quot;</span>).newInstance();</span><br><span class="line"><span class="comment">//创建一个设置JDBC连接属性的properties对象</span></span><br><span class="line"><span class="type">Properties</span> <span class="variable">props</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Properties</span>();</span><br><span class="line">props.setProperty(<span class="string">&quot;user&quot;</span>,user);</span><br><span class="line">props.setProperty(<span class="string">&quot;password&quot;</span>,pass);</span><br><span class="line">conn = driver.connect(url,props);<span class="comment">//连接数据库</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> conn;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception&#123;</span><br><span class="line">System.out.println(getConn(<span class="string">&quot;jdbc:mysql://localhost:3306/mysql&quot;</span>),<span class="string">&quot;root&quot;</span>,<span class="string">&quot;123456&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;h4 id=&quot;Java类加载&quot;&gt;&lt;a href=&quot;#Java类加载&quot; class=&quot;headerlink&quot; title=&quot;Java类加载&quot;&gt;&lt;/a&gt;Java类加载&lt;/h4&gt;&lt;p&gt;这部分知识比较深入底层，将重点介绍类加载和反射，会提到JDK动态代理、&lt;code&gt;AOP&lt;/code&gt;，反射等诸多知识点。当调用Java命令允许程序时，该命令会启动多个线程，它们都处于该Java虚拟机进程里。所有线程、变量处于同一个进程里，它们都使用JVM进程的内存区。当出现以下情况，进程将终止：</summary>
    
    
    
    <category term="java" scheme="https://goodzou.github.io/categories/java/"/>
    
    
    <category term="java" scheme="https://goodzou.github.io/tags/java/"/>
    
    <category term="类加载" scheme="https://goodzou.github.io/tags/%E7%B1%BB%E5%8A%A0%E8%BD%BD/"/>
    
  </entry>
  
  <entry>
    <title>Java注解篇</title>
    <link href="https://goodzou.github.io/2020/1016/"/>
    <id>https://goodzou.github.io/2020/1016/</id>
    <published>2020-10-16T09:19:31.000Z</published>
    <updated>2026-02-11T15:23:56.805Z</updated>
    
    <content type="html"><![CDATA[<h4 id="Java注解"><a href="#Java注解" class="headerlink" title="Java注解"></a>Java注解</h4><p>从<code>Java5</code>开始，Java增加对元数据的支持，也就是<code>Annotation</code>，不是一般的注释。这些标记在编译、类加载、运行时被读取，并执行相应处理。通过使用注解，开发人员在源文件中嵌入一些补充信息，进而代码分析和部署工具可以通过这些补充信息进行部署<span id="more"></span>。某方面看，<code>Annotation</code>就像修饰符一样，可用于修饰包、方法和构造器、变量等，这些信息被储存在<code>Annotation</code>的”name&#x3D;value”对中。</p><p><code>Annotation</code>是一个接口，程序可以通反射来获取指定程序元素的<code>Annotation</code>对象，然后通过注解对象来取得里面的元数据。</p><h4 id="基本注解"><a href="#基本注解" class="headerlink" title="基本注解"></a>基本注解</h4><p>注解必须使用工具提取元数据，工具还会根据元数据增加额外的功能，这种处理访问注解的工具统称<code>APT</code>。5个注解都在<code>Java.lang</code>包下，5个基本注解如下：</p><ul><li>@Override</li><li>@Deprecated</li><li>@SuppressWarnings</li><li>@SafeVarargs(Java7新增)</li><li>@FunctionalInterface(Java8新增)</li></ul><h5 id="限定重写父类：-Override"><a href="#限定重写父类：-Override" class="headerlink" title="限定重写父类：@Override"></a>限定重写父类：@Override</h5><p><code>@Override</code>就是用来指定方法覆盖的，它强制一个子类必须重写父类方法，告诉编译器检查这个方法，避免低级错误。注意的是：<code>@Override</code>只能修饰<code>方法</code>，不能修饰其他程序元素！</p><h5 id="标识已过时：-Deprecated"><a href="#标识已过时：-Deprecated" class="headerlink" title="标识已过时：@Deprecated"></a>标识已过时：@Deprecated</h5><p>该注解表示<code>类、方法、接口</code>已过时，当其他程序使用已过时的类、方法时，编译器将警告。</p><h5 id="抑制编译器警告：-SuppressWarnings"><a href="#抑制编译器警告：-SuppressWarnings" class="headerlink" title="抑制编译器警告：@SuppressWarnings"></a>抑制编译器警告：@SuppressWarnings</h5><p>顾名思义，注解让编译器不会发出警告，如果修饰在类上，则该类不会有任何警告出现，如果修饰方法，则该方法不会有任何警告出现。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SuppressWarnings(value=&quot;unchecked&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SuppressTest</span>&#123;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span>&#123;</span><br><span class="line">List&lt;String&gt; myList = <span class="keyword">new</span> <span class="title class_">ArrayList</span>();</span><br><span class="line">&#125;<span class="comment">//此处泛型语法警告将被抑制</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="Java7堆污染警告和-SafeVarargs"><a href="#Java7堆污染警告和-SafeVarargs" class="headerlink" title="Java7堆污染警告和@SafeVarargs"></a>Java7堆污染警告和@SafeVarargs</h5><p>上次讲泛型的时候，把一个不带泛型的对象赋给一个带泛型的变量时，将有可能引起转换异常，这种错误的原因可以称为<code>堆污染</code>。有些时候，开发者不希望看到该警告，可以用以下三种方法抑制警告：</p><ul><li>@SafeVarargs修饰该方法或构造器</li><li>@SuppressWarnings(“unchecked”)</li><li>编译使用<code>-Xlint:varargs</code></li></ul><h5 id="Java8的函数式接口与-FunctonalInterface"><a href="#Java8的函数式接口与-FunctonalInterface" class="headerlink" title="Java8的函数式接口与@FunctonalInterface"></a>Java8的函数式接口与@FunctonalInterface</h5><p>Java8规定：如果接口中只有一个<code>抽象方法</code>，该接口就是<code>函数式接口</code>。而该注解就是用来指定某个<code>接口</code>必须为函数式接口的，只能含有一个<code>抽象方法</code>。注意：该注解只能修饰接口！</p><h4 id="元注解"><a href="#元注解" class="headerlink" title="元注解"></a>元注解</h4><p>JDK除了5个基本注解外，还提供了6个元注解，其中5个都用于修饰其他注解定义，其中的<code>@Repeatable</code>专门用于定义<code>Java8</code>新增的重复注解。</p><h5 id="Retention"><a href="#Retention" class="headerlink" title="@Retention"></a>@Retention</h5><p>只能用于修饰注解定义，用于指定注解可以保留多长时间。<code>@Retention</code>包含一个<code>RetentionPolicy</code>类型的<code>Value</code>成员变量，使用时指定值。<code>value</code>成员变量的值只能是如下三个：</p><ul><li>RetentionPolicy.CLASS：编译器把注解记录在<code>class</code>文件中，当运行程序时，JVM不可获取注解信息。默认值！</li><li>RetentionPolicy.RUNTIME：编译器把注解记录在<code>class</code>文件中，当运行程序时，JVM可以获取注解信息，程序可以通过反射获取注解信息。</li><li>RetentionPolicy.SOURCE：注解只保留在源代码中，编译器之间丢弃。</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="meta">@Retention(value=RetentionPolicy.RUNTIME)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Test</span>&#123;...&#125;</span><br><span class="line"><span class="comment">//@Retention(RetentionPolicy.RUNTIME) 也可以,当成员变量为value时，可直接填入值</span></span><br></pre></td></tr></table></figure><h5 id="Target"><a href="#Target" class="headerlink" title="@Target"></a>@Target</h5><p>只能修饰一个注解定义，用于指定被修饰的注解能用于修饰哪些程序单元。。同样它也包含一个名为<code>value</code>的成员变量，其值有：</p><ul><li>ElementType.ANNOTATION_TYPE：指定注解只能修饰注解</li><li>ElementType.CONSTRUCTOR：指定该注解只能修饰构造器</li><li>ElementType.FIELD：只能修饰成员变量</li><li>ElementType.LOCAL_VARIABLE：只能修饰局部变量</li><li>ElementType.METHOD：只能修饰方法</li><li>ElementType.PACKAGE：只能修饰包</li><li>ElementType.PARAMETER：修饰参数</li><li>ElementType.TYPE：可以修饰类、接口、注解或枚举定义</li></ul><p>同样的操作，在括号内指定<code>value</code>值，可以省去<code>name=value</code>形式，直接填入值。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Target(ElementType.FIELD)</span><span class="comment">//只能修饰成员变量</span></span><br></pre></td></tr></table></figure><h5 id="Documented"><a href="#Documented" class="headerlink" title="@Documented"></a>@Documented</h5><p>指定修饰的<code>Annotation类</code>将被<code>javadoc</code>工具提取成文档，如果定义<code>Annotation类</code>使用了该注解，则所有使用该<code>Annotation</code>修饰的程序元素的API文档中将包含该<code>Annotation</code>说明。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</span></span><br><span class="line"><span class="meta">@Target(ElementType.METHOD)</span></span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> Test&#123;...&#125;</span><br><span class="line"><span class="comment">//自定义一个注解，该注解信息将被提取进入API文档</span></span><br></pre></td></tr></table></figure><h5 id="Inherited"><a href="#Inherited" class="headerlink" title="@Inherited"></a>@Inherited</h5><p>指定被它修饰的<code>Annotation</code>具有继承性，即某个类使用了该<code>Annotation</code>，则其子类将自动被该<code>Annotation</code>修饰。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Target(ElementType.TYPE)</span></span><br><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</span></span><br><span class="line"><span class="meta">@Inherited</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> i&#123;...&#125;</span><br><span class="line"><span class="comment">//使用i注解的类，其子类自动含有该注解</span></span><br></pre></td></tr></table></figure><h4 id="自定义注解"><a href="#自定义注解" class="headerlink" title="自定义注解"></a>自定义注解</h4><p>上面已经提到了自定义注解，跟定义接口差不多，只不过在<code>interface</code>前添加@符号。通常自定义注解可以修饰任何程序元素，比如类、接口、方法等。然而我们可以根据<code>Annotation</code>是否包含成员变量，可以分为两类：</p><ul><li>标记注解：没有成员变量，仅提供标记信息。</li><li>元数据注解：包含成员变量的<code>Annotation</code>，它们可以接收更多的元数据。</li></ul><p>一旦在注解内定义成员变量后，使用时就应该指定成员变量的值，或者直接在注解内指定默认值，可使用<code>default</code>关键字。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> MyTag&#123;</span><br><span class="line">String <span class="title function_">name</span><span class="params">()</span> <span class="keyword">default</span> <span class="string">&quot;James&quot;</span>;</span><br><span class="line"><span class="type">int</span> <span class="title function_">age</span><span class="params">()</span> <span class="keyword">default</span> <span class="number">32</span>;</span><br><span class="line">    <span class="comment">//int[] ages();</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Test</span>&#123;</span><br><span class="line"><span class="meta">@MyTag</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> test&#123;</span><br><span class="line">...</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="提取注解信息"><a href="#提取注解信息" class="headerlink" title="提取注解信息"></a>提取注解信息</h4><p>Java使用<code>Annotation</code>接口来代表程序元素前的注解，该接口是所有注解的的父接口，同时在<code>java.lang.reflect</code>包下新增<code>AnnotatedElement</code>接口，该接口表示可以接收注解的程序元素。该接口的实现类主要有：</p><ul><li>Class：类定义</li><li>Constructor：构造器定义</li><li>Field：类的成员变量</li><li>Method：类的方法</li><li>Package：类的包</li></ul><p>包同时还包含一些反射功能的工具类，<code>AnnotatedElement</code>接口是所有程序元素的父接口，所以程序通过反射获取某个类的<code>AnnotatedElement</code>对象之后，就可以调用对象的几个方法来访问<code>Anntation</code>信息。</p><ul><li><A extends Anntation> A getAnnotation(Class<A> annotationClass)：返回指定类型的注解，不存在返回<code>null</code></li><li><A extends Anntation> A getDeclaredAnnotations(Class<A> annotationClass)：获取直接修饰该程序元素、指定类型的<code>Annotation</code>，不存在返回<code>null</code>。</li><li>Annotation[ ] getAnnotation()：返回该程序元素上存在的所有注解</li><li>Annotation[ ] getDeclaredAnnotations()：返回直接修饰该程序元素的指定<code>Annotation</code>。</li><li>boolean isAnnotationPresent(Class&lt;? extends Annotation&gt; annotationClass)：判断该程序元素上是否有指定类型的注解。</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</span></span><br><span class="line"><span class="meta">@Target(ElementType.METHOD)</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> Testable&#123;&#125;</span><br></pre></td></tr></table></figure><p>上面的<code>@Testable</code>注解用于标记哪些方法是可以测试的，该注解可以作为<code>JUnit</code>测试的补充。</p><h4 id="Java8新增注解"><a href="#Java8新增注解" class="headerlink" title="Java8新增注解"></a>Java8新增注解</h4><h5 id="重复注解"><a href="#重复注解" class="headerlink" title="重复注解"></a>重复注解</h5><p>在Java8以前，同一程序元素前最多只能使用一个类型的<code>Annotation</code>，如果需要使用多个相同类型的注解，则必须使用<code>Annotation</code>容器。Java8以前为如下形式：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Results(&#123;@Result(name=&quot;James&quot;,location=&quot;james.jsp&quot;),@Result(name=&quot;Kameron&quot;,location=&quot;kameron.jsp&quot;)&#125;)</span></span><br></pre></td></tr></table></figure><p><code>@Results</code>注解只包含一个名为<code>value</code>、类型为<code>Result[]</code>的成员变量，指定多个<code>@Result</code>作为<code>value</code>的属性的数组元素。改进后为：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Result(name=&quot;James&quot;,location=&quot;james.jsp&quot;)</span></span><br><span class="line"><span class="meta">@Result(name=&quot;Kameron&quot;,location=&quot;kameron.jsp&quot;)</span></span><br></pre></td></tr></table></figure><p>上述即为重复注解，可以被多次使用修饰某一程序元素。开发此类重复注解需要使用<code>@Repeatable</code>修饰。</p><h5 id="新增TypeAnnotation注解"><a href="#新增TypeAnnotation注解" class="headerlink" title="新增TypeAnnotation注解"></a>新增<code>TypeAnnotation</code>注解</h5><p>Java8为<code>ElementType</code>枚举增加了<code>TYPE_PARAMETER</code>、<code>TYPE_USE</code>两个枚举值，这样允许定义枚举时使用<code>@Target(ElementType.TYPE_USE)</code>修饰，这种注解被称为<code>TypeAnnotation 类型注解</code>，<code>TypeAnnotation</code>可用在任何用到类型的地方。比如：</p><ul><li>创建对象时(new)</li><li>类型转换时</li><li>实现接口时</li><li><code>throws</code>声明抛出异常时</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Target(ElementType.TYPE_USE)</span></span><br><span class="line"><span class="meta">@NotNull</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TypeTest</span>&#123;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(<span class="meta">@NotNull</span> String[] args)</span> <span class="keyword">throws</span> <span class="meta">@NotNull</span> FileNotFoundException&#123;</span><br><span class="line"><span class="type">Object</span> <span class="variable">a</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="meta">@NotNull</span> String(<span class="string">&quot;菜鸡一号&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">foo</span><span class="params">(List&lt;<span class="meta">@NotNull</span> String)</span> info)&#123;...&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>要想让这些<code>TypeAnnotation</code>起作用，开发者需要自己实现<code>Type Annotation</code>检查框架或者使用第三方框架。</p><h4 id="编译时处理注解"><a href="#编译时处理注解" class="headerlink" title="编译时处理注解"></a>编译时处理注解</h4><p><code>APT(Annotation Processing Tool)</code>是一种注解处理工具，它对源代码进行检查，并找出源文件所包含的<code>Annotation</code>信息，做额外处理。使用<code>APT</code>的目的是简化开发者的工作量，因为APT可以在编译程序源代码的同时生成一些附属文件，比如源文件、类文件、XML文件等，内容与源代码文件相关。简而言之，APT可以替代对代码信息和附属文件的维护工作。</p><p><code>Java.exe</code>工具有一个<code>-processor</code>选项，可指定一个<code>Annotation</code>处理器，如果指定了这个处理器，那么这个处理器会在编译时提取并处理源文件中的注解。每个注解处理器都需要实现<code>javax.anntation.processing</code>包下的<code>Processor</code>接口。不过实现该接口必须实现所有方法，因此会采用继承<code>AbstractProcessor</code>方式来实现注解处理器。一个处理器可以处理一种或多种<code>Annotation</code>类型！</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">javac -processor HibernateAnnotationProcessor Person.java</span><br><span class="line"><span class="comment">//路径下生成一个Person.hbm.xml文件</span></span><br></pre></td></tr></table></figure><p><code>XML</code>文件根据源代码文件中的<code>Annotation</code>生成，这是使用APT的结果！</p>]]></content>
    
    
    <summary type="html">&lt;h4 id=&quot;Java注解&quot;&gt;&lt;a href=&quot;#Java注解&quot; class=&quot;headerlink&quot; title=&quot;Java注解&quot;&gt;&lt;/a&gt;Java注解&lt;/h4&gt;&lt;p&gt;从&lt;code&gt;Java5&lt;/code&gt;开始，Java增加对元数据的支持，也就是&lt;code&gt;Annotation&lt;/code&gt;，不是一般的注释。这些标记在编译、类加载、运行时被读取，并执行相应处理。通过使用注解，开发人员在源文件中嵌入一些补充信息，进而代码分析和部署工具可以通过这些补充信息进行部署</summary>
    
    
    
    <category term="java" scheme="https://goodzou.github.io/categories/java/"/>
    
    
    <category term="java" scheme="https://goodzou.github.io/tags/java/"/>
    
    <category term="注解" scheme="https://goodzou.github.io/tags/%E6%B3%A8%E8%A7%A3/"/>
    
  </entry>
  
</feed>
