作者:じ☆ve不哭
## Windows Batch ### 0x00 命令一览 help | more ``` >help | more 有关某个命令的详细信息,请键入 HELP 命令名 ASSOC 显示或修改文件扩展名关联。 ATTRIB 显示或更改文件属性。 BREAK 设置或清除扩展式 CTRL+C 检查。 BCDEDIT 设置启动数据库中的属性以控制启动加载。 CACLS 显示或修改文件的访问控制列表(ACL)。 CALL 从另一个批处理程序调用这一个。 CD 显示当前目录的名称或将其更改。 CHCP 显示或设置活动代码页数。 CHDIR 显示当前目录的名称或将其更改。 CHKDSK 检查磁盘并显示状态报告。 CHKNTFS 显示或修改启动时间磁盘检查。 CLS 清除屏幕。 CMD 打开另一个 Windows 命令解释程序窗口。 COLOR 设置默认控制台前景和背景颜色。 COMP 比较两个或两套文件的内容。 COMPACT 显示或更改 NTFS 分区上文件的压缩。 CONVERT 将 FAT 卷转换成 NTFS。你不能转换当前驱动器。 COPY 将至少一个文件复制到另一个位置。 DATE 显示或设置日期。 DEL 删除至少一个文件。 DIR 显示一个目录中的文件和子目录。 DISKPART 显示或配置磁盘分区属性。 DOSKEY 编辑命令行、撤回 Windows 命令并创建宏。 DRIVERQUERY 显示当前设备驱动程序状态和属性。 ECHO 显示消息,或将命令回显打开或关闭。 ENDLOCAL 结束批文件中环境更改的本地化。 ERASE 删除一个或多个文件。 EXIT 退出 CMD.EXE 程序(命令解释程序)。 FC 比较两个文件或两个文件集并显示它们之间的不同。 FIND 在一个或多个文件中搜索一个文本字符串。 FINDSTR 在多个文件中搜索字符串。 FOR 为一组文件中的每个文件运行一个指定的命令。 FORMAT 格式化磁盘,以便用于 Windows。 FSUTIL 显示或配置文件系统属性。 FTYPE 显示或修改在文件扩展名关联中使用的文件类型。 GOTO 将 Windows 命令解释程序定向到批处理程序中某个带标签的行。 GPRESULT 显示计算机或用户的组策略信息。 GRAFTABL 使 Windows 在图形模式下显示扩展字符集。 HELP 提供 Windows 命令的帮助信息。 ICACLS 显示、修改、备份或还原文件和目录的 ACL。 IF 在批处理程序中执行有条件的处理操作。 LABEL 创建、更改或删除磁盘的卷标。 MD 创建一个目录。 MKDIR 创建一个目录。 MKLINK 创建符号链接和硬链接 MODE 配置系统设备。 MORE 逐屏显示输出。 MOVE 将一个或多个文件从一个目录移动到另一个目录。 OPENFILES 显示远程用户为了文件共享而打开的文件。 PATH 为可执行文件显示或设置搜索路径。 PAUSE 暂停批处理文件的处理并显示消息。 POPD 还原通过 PUSHD 保存的当前目录的上一个值。 PRINT 打印一个文本文件。 PROMPT 更改 Windows 命令提示。 PUSHD 保存当前目录,然后对其进行更改。 RD 删除目录。 RECOVER 从损坏的或有缺陷的磁盘中恢复可读信息。 REM 记录批处理文件或 CONFIG.SYS 中的注释(批注)。 REN 重命名文件。 RENAME 重命名文件。 REPLACE 替换文件。 RMDIR 删除目录。 ROBOCOPY 复制文件和目录树的高级实用工具 SET 显示、设置或删除 Windows 环境变量。 SETLOCAL 开始本地化批处理文件中的环境更改。 SC 显示或配置服务(后台进程)。 SCHTASKS 安排在一台计算机上运行命令和程序。 SHIFT 调整批处理文件中可替换参数的位置。 SHUTDOWN 允许通过本地或远程方式正确关闭计算机。 SORT 对输入排序。 START 启动单独的窗口以运行指定的程序或命令。 SUBST 将路径与驱动器号关联。 SYSTEMINFO 显示计算机的特定属性和配置。 TASKLIST 显示包括服务在内的所有当前运行的任务。 TASKKILL 中止或停止正在运行的进程或应用程序。 TIME 显示或设置系统时间。 TITLE 设置 CMD.EXE 会话的窗口标题。 TREE 以图形方式显示驱动程序或路径的目录结构。 TYPE 显示文本文件的内容。 VER 显示 Windows 的版本。 VERIFY 告诉 Windows 是否进行验证,以确保文件正确写入磁盘。 VOL 显示磁盘卷标和序列号。 XCOPY 复制文件和目录树。 WMIC 在交互式命令 shell 中显示 WMI 信息。 ``` 如何查看命令解释----- 命令 help [子命令] 或者命令 /? ### 0x01批处理基础 #### 0x0100 常用批处理内部命令简介 ##### 预热一下 ``` @echo off rem say hello echo hello batch pause ``` ##### REM 和 :: 注释 - rem 可回显 - :: 不回显 ##### ECHO 和 @ 回显 及隐藏命令 ``` >help echo 显示消息,或者启用或关闭命令回显。 ECHO [ON | OFF] ECHO [message] ``` @字符放在命令前将关闭该命令回显,无论此时 echo 是否为打开状态。 通常以@echo off作为批处理程序的首行 ##### PAUSE 暂停 ``` C:\Users\Leon>pause 请按任意键继续. . . :: 修改提示语 >echo 继续 & pause>nul 继续 ``` ##### ERRORLEVEL ``` :: 默认是0 无效命令时值为9009 >echo %errorlevel% 0 >dd 'dd' 不是内部或外部命令,也不是可运行的程序 或批处理文件。 >echo %errorlevel% 9009 ``` ##### TITLE 修改标题 ``` >title hello :: 注意窗口的标题变化 ``` ##### COLOR > 设置默认的控制台前景和背景颜色。 > > COLOR [attr] > > attr 指定控制台输出的颜色属性。 > > 颜色属性由两个十六进制数字指定 -- 第一个 > 对应于背景,第二个对应于前景。每个数字 > 可以为以下任何值: > > 0 = 黑色 8 = 灰色 > 1 = 蓝色 9 = 淡蓝色 > 2 = 绿色 A = 淡绿色 > 3 = 浅绿色 B = 淡浅绿色 > 4 = 红色 C = 淡红色 > 5 = 紫色 D = 淡紫色 > 6 = 黄色 E = 淡黄色 > 7 = 白色 F = 亮白色 > > 如果没有给定任何参数,此命令会将颜色还原到 CMD.EXE 启动时 > 的颜色。这个值来自当前控制台 > 窗口、/T 命令行开关或 DefaultColor 注册表 > 值。 > > 如果尝试使用相同的 > 前景和背景颜色来执行 > COLOR 命令,COLOR 命令会将 ERRORLEVEL 设置为 1。 ``` >color fc :: 注意命令窗口的背景及字体变化 ``` ##### mode 配置系统设备 > 配置系统设备。 > > 串行端口: MODE COMm[:] [BAUD=b] [PARITY=p] [DATA=d] [STOP=s] > [to=on|off] [xon=on|off] [odsr=on|off] > [octs=on|off] [dtr=on|off|hs] > [rts=on|off|hs|tg] [idsr=on|off] > > 设备状态: MODE [device] [/STATUS] > > 打印重定向: MODE LPTn[:]=COMm[:] > > 选择代码页: MODE CON[:] CP SELECT=yyy > > 代码页状态: MODE CON[:] CP [/STATUS] > > 显示模式: MODE CON[:] [COLS=c] [LINES=n] > > 击键率: MODE CON[:] [RATE=r DELAY=d] ``` >mode con cols=113 lines=15 & color 9f ::此命令设置 DOS 窗口大小:15 行,113 列 ``` ##### GOTO 和 : 跳转标签 > 将 cmd.exe 定向到批处理程序中带标签的行。 > > GOTO label > > label 指定批处理程序中用作标签的文字字符串。 > > 标签必须单独一行,并且以冒号打头。 > > 如果命令扩展被启用,GOTO 会如下改变: > > GOTO 命令现在接受目标标签 :EOF,这个标签将控制转移到当前 > 批脚本文件的结尾。不定义就退出批脚本文件,这是一个容易的 > 办法。有关能使该功能有用的 CALL 命令的扩展描述,请键入 ``` @echo off :start set /a var+=1 echo %var% if %var% leq 3 GOTO start pause :: 输出 1 2 3 4 ``` ##### FIND > 在文件中搜索字符串。 > > FIND [/V] [/C] [/N] [/I] [/OFF[LINE]] "string" [[drive:][path]filename[ ...]] > > /V 显示所有未包含指定字符串的行。 > /C 仅显示包含字符串的行数。 > /N 显示行号。 > /I 搜索字符串时忽略大小写。 > /OFF[LINE] 不要跳过具有脱机属性集的文件。 > "string" 指定要搜索的文本字符串。 > [drive:][path]filename > 指定要搜索的文件。 > > 如果没有指定路径,FIND 将搜索在提示符处键入 > 的文本或者由另一命令产生的文本。 find 常与type 一起使用 ``` @echo off echo 111 >test.txt echo 222 >>test.txt find "111" test.txt type test.txt|find "222" pause del test.txt pause ``` ##### FINDSTR > 在文件中寻找字符串。 > > FINDSTR [/B] [/E] [/L] [/R] [/S] [/I] [/X] [/V] [/N] [/M] [/O] [/P] [/F:file] > [/C:string] [/G:file] [/D:dir list] [/A:color attributes] [/OFF[LINE]] > strings [[drive:][path]filename[ ...]] > > /B 在一行的开始配对模式。 > /E 在一行的结尾配对模式。 > /L 按字使用搜索字符串。 > /R 将搜索字符串作为一般表达式使用。 > /S 在当前目录和所有子目录中搜索匹配文件。 > /I 指定搜索不分大小写。 > /X 打印完全匹配的行。 > /V 只打印不包含匹配的行。 > /N 在匹配的每行前打印行数。 > /M 如果文件含有匹配项,只打印其文件名。 > /O 在每个匹配行前打印字符偏移量。 > /P 忽略有不可打印字符的文件。 > /OFF[LINE] 不跳过带有脱机属性集的文件。 > /A:attr 指定有十六进位数字的颜色属性。请见 "color /?" > /F:file 从指定文件读文件列表 (/ 代表控制台)。 > /C:string 使用指定字符串作为文字搜索字符串。 > /G:file 从指定的文件获得搜索字符串。 (/ 代表控制台)。 > /D:dir 查找以分号为分隔符的目录列表 > strings 要查找的文字。 > [drive:][path]filename > 指定要查找的文件。 > > 除非参数有 /C 前缀,请使用空格隔开搜索字符串。 > 例如: 'FINDSTR "hello there" x.y' 在文件 x.y 中寻找 "hello" 或 > "there"。'FINDSTR /C:"hello there" x.y' 文件 x.y 寻找 > "hello there"。 > > 一般表达式的快速参考: > . 通配符: 任何字符 > * 重复: 以前字符或类出现零或零以上次数 > ^ 行位置: 行的开始 > $ 行位置: 行的终点 > [class] 字符类: 任何在字符集中的字符 > [^class] 补字符类: 任何不在字符集中的字符 > [x-y] 范围: 在指定范围内的任何字符 > \x Escape: 元字符 x 的文字用法 > \<xyz 字位置: 字的开始 > xyz\> 字位置: 字的结束 > > 有关 FINDSTR 常见表达法的详细情况,请见联机命令参考。 ``` findstr "111" test.txt type test.txt|findstr "222" ``` ##### START > 启动一个单独的窗口以运行指定的程序或命令。 ``` >start cmd :: 打开命令行 ``` ##### assoc 和 ftype 显示或修改文件扩展名关联 > ASSOC [.ext[=[fileType]]] > > .ext 指定跟文件类型关联的文件扩展名 > fileType 指定跟文件扩展名关联的文件类型 > > 键入 ASSOC 而不带参数,显示当前文件关联。如果只用文件扩展 > 名调用 ASSOC,则显示那个文件扩展名的当前文件关联。如果不为 > 文件类型指定任何参数,命令会删除文件扩展名的关联。 ``` assoc #显示所有'文件扩展名'关联 assoc.txt #显示.txt 代表的'文件类型',结果显示 .txt=txtfile assoc.doc #显示.doc代表的'文件类型',结果显示 .doc=Word.Document.8 assoc.exe #显示.exe代表的'文件类型',结果显示 .exe=exefile ftype #显示所有'文件类型'关联 ftypeexefile #显示 exefile 类型关联的命令行,结果显示 exefile="%1" %* assoc.txt=Word.Document.8 设置.txt 为 word 类型的文档,可以看到.txt 文件的图标都变了 assoc.txt=txtfile 恢复.txt 的正确关联 ftypeexefile="%1" %* 恢复 exefile 的正确关联 如果该关联已经被破坏,可以运行 command.com , ``` ##### pushd 和 popd 切换当前目录 ``` @echo off c: & cd\ & md mp3 #在 C:\ 建立 mp3 文件夹 md d:\mp4 #在 D:\ 建立 mp4 文件夹 cd /d d:\mp4 #更改当前目录为 d:\mp4 pushd c:\mp3 #保存当前目录,并切换当前目录为 c:\mp3 popd #恢复当前目录为刚才保存的 d:\mp4 一般用处不大,在当前目录名不确定时,会有点帮助。(dos 编程中很有用) ``` ##### CALL > 从批处理程序调用另一个批处理程序。 > > CALL [drive:][path]filename [batch-parameters] > > batch-parameters 指定批处理程序所需的命令行信息。 > > 如果命令扩展被启用,CALL 会如下改变: > > CALL 命令现在将卷标当作 CALL 的目标接受。语法是: > > CALL:label arguments > > 一个新的批文件上下文由指定的参数所创建,控制在卷标被指定 > 后传递到语句。你必须通过达到批脚本文件末两次来 "exit" 两次。 > 第一次读到文件末时,控制会回到 CALL 语句的紧后面。第二次 > 会退出批脚本。键入 GOTO /?,参看 GOTO :EOF 扩展的描述, > 此描述允许你从一个批脚本返回。 > > 另外,批脚本文本参数参照(%0、%1、等等)已如下改变: > > > 批脚本里的 %* 指出所有的参数(如 %1 %2 %3 %4 %5 ...) > > 批参数(%n)的替代已被增强。你可以使用以下语法: > > %~1 - 删除引号("),扩展 %1 > %~f1 - 将 %1 扩展到一个完全合格的路径名 > %~d1 - 仅将 %1 扩展到一个驱动器号 > %~p1 - 仅将 %1 扩展到一个路径 > %~n1 - 仅将 %1 扩展到一个文件名 > %~x1 - 仅将 %1 扩展到一个文件扩展名 > %~s1 - 扩展的路径只含有短名 > %~a1 - 将 %1 扩展到文件属性 > %~t1 - 将 %1 扩展到文件的日期/时间 > %~z1 - 将 %1 扩展到文件的大小 > %~$PATH:1 - 查找列在 PATH 环境变量的目录,并将 %1 > 扩展到找到的第一个完全合格的名称。如果 > 环境变量名未被定义,或者没有找到文件, > 此修改符会扩展到空字符串 > > 可以组合修改符来取得多重结果: > > %~dp1 - 只将 %1 扩展到驱动器号和路径 > %~nx1 - 只将 %1 扩展到文件名和扩展名 > %~dp$PATH:1 - 在列在 PATH 环境变量中的目录里查找 %1, > 并扩展到找到的第一个文件的驱动器号和路径。 > %~ftza1 - 将 %1 扩展到类似 DIR 的输出行。 > > 在上面的例子中,%1 和 PATH 可以被其他有效数值替换。 > %~ 语法被一个有效参数号码终止。%~ 修定符不能跟 %*使用 ``` @echo off chcp 65001 Echo 产生一个临时文件 > tmp.txt Rem 下行先保存当前目录,再将 c:\windows设为当前目录 pushd c:\windows Call :sub tmp.txt Rem 下行恢复前次的当前目录 Popd Call :sub tmp.txt pause Del tmp.txt :sub Echo 删除引号: %~1 Echo 扩充到路径: %~f1 Echo 扩充到一个驱动器号: %~d1 Echo 扩充到一个路径: %~p1 Echo 扩充到一个文件名: %~n1 Echo 扩充到一个文件扩展名: %~x1 Echo 扩充的路径指含有短名: %~s1 Echo 扩充到文件属性: %~a1 Echo 扩充到文件的日期/时间: %~t1 Echo 扩充到文件的大小: %~z1 Echo 扩展到驱动器号和路径:%~dp1 Echo 扩展到文件名和扩展名:%~nx1 Echo 扩展到类似 DIR 的输出行:%~ftza1 Echo. Goto :eof ``` ##### shift > 更改批处理文件中可替换参数的位置。 > > SHIFT [/n] > > 如果命令扩展被启用,SHIFT 命令支持/n 命令行开关;该命令行开关告诉 > 命令从第 n 个参数开始移位;n 介于零和八之间。例如: > > SHIFT /2 > > 会将 %3 移位到 %2,将 %4 移位到 %3,等等;并且不影响 %0 和 %1。 ##### IF > 执行批处理程序中的条件处理。 > > IF [NOT] ERRORLEVEL number command > IF [NOT] string1==string2 command > IF [NOT] EXIST filen > > ame command > > NOT 指定只有条件为 false 的情况下,Windows 才 > 应该执行该命令。 > > ERRORLEVEL number 如果最后运行的程序返回一个等于或大于 > 指定数字的退出代码,指定条件为 true。 > > string1==string2 如果指定的文字字符串匹配,指定条件为 true。 > > EXIST filename 如果指定的文件名存在,指定条件为 true。 > > command 如果符合条件,指定要执行的命令。如果指定的 > 条件为 FALSE,命令后可跟 ELSE 命令,该命令将 > 在 ELSE 关键字之后执行该命令。 > > ELSE 子句必须出现在同一行上的 IF 之后。例如: > > IF EXIST filename. ( > del filename. > ) ELSE ( > echo filename. missing. > ) > > 由于 del 命令需要用新的一行终止,因此以下子句不会有效: > > IF EXIST filename. del filename. ELSE echo filename. missing > > 由于 ELSE 命令必须与 IF 命令的尾端在同一行上,以下子句也 > 不会有效: > > IF EXIST filename. del filename. > ELSE echo filename. missing > 如果都放在同一行上,以下子句有效: > > IF EXIST filename. (del filename.) ELSE echo filename. missing > > 如果命令扩展被启用,IF 会如下改变: > > IF [/I] string1 compare-op string2 command > IF CMDEXTVERSION number command > IF DEFINED variable command > > 其中, compare-op 可以是: > > EQU - 等于 > NEQ - 不等于 > LSS - 小于 > LEQ - 小于或等于 > GTR - 大于 > GEQ - 大于或等于 > > 而 /I 开关(如果指定)说明要进行的字符串比较不分大小写。 > /I 开关可以用于 IF 的 string1==string2 的形式上。这些 > 比较都是通用的;原因是,如果 string1 和 string2 都是 > 由数字组成的,字符串会被转换成数字,进行数字比较。 > > CMDEXTVERSION 条件的作用跟 ERRORLEVEL 的一样,除了它 > 是在跟与命令扩展有关联的内部版本号比较。第一个版本 > 是 1。每次对命令扩展有相当大的增强时,版本号会增加一个。 > 命令扩展被停用时,CMDEXTVERSION 条件不是真的。 > > 如果已定义环境变量,DEFINED 条件的作用跟 EXIST 的一样, > 除了它取得一个环境变量,返回的结果是 true。 > > 如果没有名为 ERRORLEVEL 的环境变量,%ERRORLEVEL% > 会扩充为 ERROLEVEL 当前数值的字符串表达式;否则,你会得到 > 其数值。运行程序后,以下语句说明 ERRORLEVEL 的用法: > > goto answer%ERRORLEVEL% > :answer0 > echo Program had return code 0 > :answer1 > echo Program had return code 1 > > 你也可以使用以上的数字比较: > > IF %ERRORLEVEL% LEQ 1 goto okay > > 如果没有名为 CMDCMDLINE 的环境变量,%CMDCMDLINE% > 将在 CMD.EXE 进行任何处理前扩充为传递给 CMD.EXE 的原始 > 命令行;否则,你会得到其数值。 > > 如果没有名为 CMDEXTVERSION 的环境变量, > %CMDEXTVERSION% 会扩充为 CMDEXTVERSION 当前数值的 > 字串符表达式;否则,你会得到其数值。 ##### setlocal 与 变量延迟 > 开始批处理文件中环境改动的本地化操作。在执行 SETLOCAL 之后 > 所做的环境改动只限于批处理文件。要还原原先的设置,必须执 > 行 ENDLOCAL。达到批处理文件结尾时,对于该批处理文件的每个 > 尚未执行的 SETLOCAL 命令,都会有一个隐含的 ENDLOCAL 被执行。 > > SETLOCAL > > 如果启用命令扩展,则 SETLOCAL 更改如下: > > SETLOCAL 批命令现在可以接受可选参数: > ENABLEEXTENSIONS / DISABLEEXTENSIONS > 启用或禁用命令处理器扩展。这些 > 参数比 CMD /E:ON 或 /E:OFF > 开关有优先权。请参阅 CMD /? 获取详细信息。 > ENABLEDELAYEDEXPANSION / DISABLEDELAYEDEXPANSION > 启用或禁用延缓环境变量 > 扩展。这些参数比 CMD > /V:ON 或 /V:OFF 开关有优先权。请参阅 CMD /? 获取详细信息。 > 无论在 SETLOCAL 命令之前的设置是什么,这些修改会一直 > 生效,直到出现相应的 ENDLOCAL 命令。 > > 在给定参数的情况下, > SETLOCAL 命令将设置 ERRORLEVEL 值。如果给定两个有效参数中的一个,另一个未给定, > 则该值为零。 > 通过以下方法,你可以在批脚本中 > 使用此项来确定扩展是否可用: > > VERIFY OTHER 2>nul > SETLOCAL ENABLEEXTENSIONS > IF ERRORLEVEL 1 echo Unable to enable extensions > > 此方法之所以有效,是因为在 CMD.EXE 的旧版本上,SETLOCAL > 不设置 ERRORLEVEL 值。如果参数不正确,VERIFY 命令会将 > ERRORLEVEL 值初始化为非零值。 ``` set a=4 set a=5 & echo %a% pause :: 显示4 setlocal enabledelayedexpansion set a=4 set a=5 & echo !a! ::显示5 pause for /l %%i in (1,1,5) do ( set a=%%i echo !a! ) :: 显示 1 2 3 4 5 ``` #### 0x0101 常用特殊符号 ##### 1、@ 命令行回显屏蔽符 ##### 2、% 批处理变量引导符 > 引用变量用%var%,调用程序外部参数用%1 至%9 等等 > %0 %1 %2 %3 %4 %5 %6 %7 %8 %9 %*为命令行传递给批处理的参数 > %0 批处理文件本身,包括完整的路径和扩展名 > > ``` > 例:最简单的复制文件自身的方法 > copy %0 d:\wind.bat > ``` > > ##### 3、> 重定向符 > 这个字符的意思是传递并且覆盖,他所起的作用是将运行的结果传递到后面的范围(后边可 > 以是文件,也可以是默认的系统控制台) > 在 NT 系列命令行中,重定向的作用范围由整个命令行转变为单个命令语句,受到了命 > 令分隔符&,&&,||和语句块的制约限制。 > 比如: > 使用命令:echo hello >1.txt 将建立文件 1.txt,内容为”hello “(注意行尾有一空格) > 使用命令:echo hello>1.txt 将建立文件 1.txt,内容为”hello“(注意行尾没有空格) ##### 4、>> 重定向符 追加 ``` echo hello > 1.txt echo world >>1.txt 这时候 1.txt 内容如下 : hello world ``` ##### 5、<、>&、<& 重定向符 了解 > <,输入重定向命令,从文件中读入命令输入,而不是从键盘中读入。 > > &,将一个句柄的输出写入到另一个句柄的输入中。 > > <&,刚好和>&相反,从一个句柄读取输入并将其写入到另一个句柄输出中。 > > 常用句柄:0、1、2,未定义句柄:3—9 > 1>nul 表示禁止输出正确的信息 > 2>nul 表示禁止输出错误信息。 ##### 6、| 命令管道符 > 格式:第一条命令 | 第二条命令 [| 第三条命令...] > 将第一条命令的结果作为第二条命令的参数来使用,记得在 unix 中这种方式很常见 ``` dir c:\|find "txt" dir c:\|findstr "txt" ::必须双引号 echo y|format a: /s /q /v:system ::格式化 ``` ##### 7、 ^ 转义字符 > ^ 是对特殊符号<,>,&的前导字符,在命令中他将以上 3 个符号的特殊功能去掉,仅仅只把他 > 们当成符号而不使用他们的特殊意义。 ``` echo test ^>1.txt 结果则是:test > 1.txt ``` > 另外,此转义字符还可以用作续行符号。 ``` @echo off echo 英雄 ^ 是 ^ 好 ^ 男人 pause ``` ##### 8、& 组合命令 > 语法:第一条命令 & 第二条命令 [& 第三条命令...] > &、&&、||为组合命令,顾名思义,就是可以把多个命令组合起来当一个命令来执行。这在 > 批处理脚本里是允许的,而且用的非常广泛 > > **允许在一行中使用 2 个以上不同的命令,当第一个命令执行失败了,也不影响后边** > **的命令执行。** ``` dir z:\ & dir y:\ & dir c:\ ``` ##### 9、&& 组合命令 > 语法:第一条命令 && 第二条命令 [&& 第三条命令...] > > 可以同时执行多条命令,当碰到执行**出错**的命令后**将不执行后面**的命令,如果一 > 直没有出错则一直执行完所有命令 ``` dir z:\ && dir y:\ && dir c:\ ``` ##### 10、|| 组合命令 > 语法:第一条命令 || 第二条命令 [|| 第三条命令...] > 可以同时执行多条命令,**当一条命令失败后才执行第二条命令**,当碰到执行正确的命令后将不执行后面的命令,如果没有出现正确的命令则一直执行完所有命令; > > 注意: 管道命令的优先级高于重定向命令 ##### 11、"" 字符串界定符 > 双引号允许在字符串中包含空格,进入一个特殊目录可以用如下方法 ``` cd "program files" cd progra~1 cd pro ``` ##### 12、, 逗号 > 逗号相当于空格,在某些情况下“,”可以用来当做空格使 ``` dir,c:\ ``` ##### 13、; 分号 > 当命令相同时,可以将不同目标用;来隔离,但执行效果不变,如执行过程中发生错 > 误,则只返回错误报告,但程序仍会执行。 ``` dir c:\;d:\;e:\;z:\ :: 找不到路径报错 dir c:\;d:\;e:\1.txt ::e盘存在,有错误提示,但命令仍会执行 ``` ##### 14、() 括号 > 小括号在批处理编程中有特殊的作用,左右括号必须成对使用,括号中可以包括多行命令 , > 这些命令将被看成一个整体,视为一条命令行。 > 括号在 for语句和 if 语句中常见,用来嵌套使用循环或条件语句,其实括号()也可以单独使用 ``` ( echo 1 echo 2 echo 3 ) 注意:这种多条命令被视为一条命令行时,如果其中有变量,就涉及到变量延迟的问题。 ``` ##### 15、! 感叹号 > 在变量延迟问题中,用来表示变量,即%var%应该表示为!var! ### 0x02 FOR 命令详解 > 对一组文件中的每一个文件执行某个特定命令。 > > FOR %variable IN (set) DO command [command-parameters] > > %variable 指定一个单一字母可替换的参数。 > (set) 指定一个或一组文件。可以使用通配符。 > command 指定对每个文件执行的命令。 > command-parameters > 为特定命令指定参数或命令行开关。 > > 在批处理程序中使用 FOR 命令时,指定变量请使用 %%variable > 而不要用 %variable。变量名称是区分大小写的,所以 %i 不同于 %I. > > 如果启用命令扩展,则会支持下列 FOR 命令的其他格式: > > FOR /D %variable IN (set) DO command [command-parameters] > > 如果集中包含通配符,则指定与目录名匹配,而不与文件名匹配。 > > FOR /R [[drive:]path] %variable IN (set) DO command [command-parameters] > > 检查以 [drive:]path 为根的目录树,指向每个目录中的 FOR 语句。 > 如果在 /R 后没有指定目录规范,则使用当前目录。如果集仅为一个单点(.)字符, > 则枚举该目录树。 > > FOR /L %variable IN (start,step,end) DO command [command-parameters] > > 该集表示以增量形式从开始到结束的一个数字序列。因此,(1,1,5)将产生序列 > 1 2 3 4 5,(5,-1,1)将产生序列(5 4 3 2 1) > > FOR /F ["options"] %variable IN (file-set) DO command [command-parameters] > FOR /F ["options"] %variable IN ("string") DO command [command-parameters] > FOR /F ["options"] %variable IN ('command') DO command [command-parameters] > > 或者,如果有 usebackq 选项: > > FOR /F ["options"] %variable IN (file-set) DO command [command-parameters] > FOR /F ["options"] %variable IN ("string") DO command [command-parameters] > FOR /F ["options"] %variable IN ('command') DO command [command-parameters] > > fileset 为一个或多个文件名。继续到 fileset 中的下一个文件之前, > 每份文件都被打开、读取并经过处理。处理包括读取文件,将其分成一行行的文字, > 然后将每行解析成零或更多的符号。然后用已找到的符号字符串变量值调用 For 循环。 > 以默认方式,/F 通过每个文件的每一行中分开的第一个空白符号。跳过空白行。 > 你可通过指定可选 "options" 参数替代默认解析操作。这个带引号的字符串包括一个 > 或多个指定不同解析选项的关键字。这些关键字为: > > eol=c - 指一个行注释字符的结尾(就一个) > skip=n - 指在文件开始时忽略的行数。 > delims=xxx - 指分隔符集。这个替换了空格和制表符的 > 默认分隔符集。 > tokens=x,y,m-n - 指每行的哪一个符号被传递到每个迭代 > 的 for 本身。这会导致额外变量名称的分配。m-n > 格式为一个范围。通过 nth 符号指定 mth。如果 > 符号字符串中的最后一个字符星号, > 那么额外的变量将在最后一个符号解析之后 > 分配并接受行的保留文本。 > usebackq - 指定新语法已在下类情况中使用: > 在作为命令执行一个后引号的字符串并且一个单 > 引号字符为文字字符串命令并允许在 file-set > 中使用双引号扩起文件名称。 > > 某些范例可能有助: > FOR /F "eol=; tokens=2,3* delims=, " %i in (myfile.txt) do @echo %i %j %k > > 会分析 myfile.txt 中的每一行,忽略以分号打头的那些行,将 > 每行中的第二个和第三个符号传递给 for 函数体,用逗号和/或 > 空格分隔符号。请注意,此 for 函数体的语句引用 %i 来 > 获得第二个符号,引用 %j 来获得第三个符号,引用 %k > 来获得第三个符号后的所有剩余符号。对于带有空格的文件 > 名,你需要用双引号将文件名括起来。为了用这种方式来使 > 用双引号,还需要使用 usebackq 选项,否则,双引号会 > 被理解成是用作定义某个要分析的字符串的。 > > %i 在 for 语句中显式声明,%j 和 %k 是通过 > tokens= 选项隐式声明的。可以通过 tokens= 一行 > 指定最多 26 个符号,只要不试图声明一个高于字母 "z" 或 > "Z" 的变量。请记住,FOR 变量是单一字母、分大小写和全局的变量; > 而且,不能同时使用超过 52 个。 > > 还可以在相邻字符串上使用 FOR /F 分析逻辑,方法是, > 用单引号将括号之间的 file-set 括起来。这样,该字符 > 串会被当作一个文件中的一个单一输入行进行解析。 > > 最后,可以用 FOR /F 命令来分析命令的输出。方法是,将 > 括号之间的 file-set 变成一个反括字符串。该字符串会 > 被当作命令行,传递到一个子 CMD.EXE,其输出会被捕获到 > 内存中,并被当作文件分析。如以下例子所示: > > FOR /F "usebackq delims==" %i IN (`set`) DO @echo %i > > 会枚举当前环境中的环境变量名称。 > > 另外,FOR 变量参照的替换已被增强。你现在可以使用下列 > 选项语法: > > %~I - 删除任何引号("),扩展 %I > %~fI - 将 %I 扩展到一个完全合格的路径名 > %~dI - 仅将 %I 扩展到一个驱动器号 > %~pI - 仅将 %I 扩展到一个路径 > %~nI - 仅将 %I 扩展到一个文件名 > %~xI - 仅将 %I 扩展到一个文件扩展名 > %~sI - 扩展的路径只含有短名 > %~aI - 将 %I 扩展到文件的文件属性 > %~tI - 将 %I 扩展到文件的日期/时间 > %~zI - 将 %I 扩展到文件的大小 > %~$PATH:I - 查找列在路径环境变量的目录,并将 %I 扩展 > 到找到的第一个完全合格的名称。如果环境变量名 > 未被定义,或者没有找到文件,此组合键会扩展到 > 空字符串 > > 可以组合修饰符来得到多重结果: > > %~dpI - 仅将 %I 扩展到一个驱动器号和路径 > %~nxI - 仅将 %I 扩展到一个文件名和扩展名 > %~fsI - 仅将 %I 扩展到一个带有短名的完整路径名 > %~dp$PATH:I - 搜索列在路径环境变量的目录,并将 %I 扩展 > 到找到的第一个驱动器号和路径。 > %~ftzaI - 将 %I 扩展到类似输出线路的 DIR > > 在以上例子中,%I 和 PATH 可用其他有效数值代替。%~ 语法 > 用一个有效的 FOR 变量名终止。选取类似 %I 的大写变量名 > 比较易读,而且避免与不分大小写的组合键混淆。 ##### 一、参数 /d > FOR /D %variable IN (set) DO command [command-parameters] > 如果集中包含通配符,则指定与目录名匹配,而不与文件名匹配。 ``` @echo off for /d %%i in (c:\*) do echo %%i :: bat 或cmd 文件 %%i , 命令行 %i pause for /d %%i in (???) do echo %%i pause ::通配符 ? 一个字符 * 任意个字符 ``` ##### 二、参数 /R > FOR /R [[drive:]path] %variable IN (set) DO command [command-parameters] > 检查以 [drive:]path 为根的目录树,指向每个目录中的FOR 语句。如果在 /R 后没有指定目录,则使用当前 > 目录。如果集仅为一个单点(.)字符,则枚举该目录树。 ``` @echo off for /r c:\ %%i in (*.exe) do echo %%i pause for /r c:\ %%i in (boot.ini) do echo %%i ::枚举了 c 盘所有目录,改为以下 pause for /r c:\ %%i in (py.exe) do if exist %%i echo %%i pause ``` ##### 三、参数 /L > FOR /L%variable IN (start,step,end) DO command [command-parameters] > > 该集表示以增量形式从开始到结束的一个数字序列。因此,(1,1,5) 将产生序列 1 2 3 4 5,(5,-1,1) 将产生序列 (5 4 3 2 1) ``` @echo off for /l %%i in (1,1,5) do @echo %%i pause ``` ##### 四、参数 /F > FOR /F ["options"] %variable IN (file-set) DO command [command-parameters] > FOR /F ["options"] %variable IN ("string") DO command [command-parameters] > FOR /F ["options"] %variable IN ('command') DO command [command-parameters > > 带引号的字符串"options"包括一个或多个指定不同解析选项的关键字。这些关键字为 : > eol=c - 指一个行注释字符的结尾(就一个) > skip=n - 指在文件开始时忽略的行数。 > delims=xxx - 指分隔符集。这个替换了空格和跳格键的默认分隔符集。 > tokens=x,y,m-n - 指每行的哪一个符号被传递到每个迭代的 for 本身。这会导致额外变量名称的分配。 > > m-n格式为一个范围。通过 nth 符号指定 mth。如果符号字符串中的最后一个字符星号, 那么额外的变量将在最后一个符号解析之后分配并接受行的保留文本。 > usebackq - 使用后引号(键盘上数字 1 左面的那个键 ` )未使用参数 usebackq 时:file-set 表示文 ` > > `件,但不能含有空格 > 双引号表示字符串,即"string" > 单引号表示执行命令,即'command' > 使用参数 usebackq 时:file-set 和"file-set"都表示文件 > 当文件路径或名称中有空格时,就可以用双引号括起来 > 单引号表示字符串,即'string' > 后引号表示命令执行,即`command` ``` @echo off rem 首先建立临时文件 test.txt echo ;注释行,这是临时文件,用完删除 >test.txt echo 11 段 12 段 13 段 14 段 15 段 16 段 >>test.txt echo 21 段,22 段,23 段,24 段,25 段,26 段 >>test.txt echo 31 段-32 段-33 段-34 段-35 段-36 段 >>test.txt FOR /F "eol=; tokens=1,3* delims=,- " %%i in (test.txt)do echo %%i %%j %%k Pause Del test.txt ``` ### 0x03 FOR 命令中的变量 ##### 一、 ~I - 删除任何引号("),扩展 %I ``` @echo off echo ^"1111>temp.txt echo "2222">>temp.txt echo 3333^">>temp.txt echo "4444"44>>temp.txt echo ^"55"55"55>>temp.txt rem 上面建立临时文件,注意不成对的引号要加转义字符 ^ ,重定向符号前不要留空格 FOR /F "delims=" %%i IN (temp.txt) DO echo %%~i pause del temp.txt 输出: 1111 2222 3333" 4444"44 55"55"55 ``` 删除引号规则如下(BAT 兄补充!) 1、若字符串首尾同时存在引号,则删除首尾的引号; 2、若字符串尾不存在引号,则删除字符串首的引号; 3、如果字符串中间存在引号,或者只在尾部存在引号,则不删除。 ##### 二、 %~fI - 将 %I 扩展到一个完全合格的路径名 ``` @echo off FOR /F "delims==" %%i IN ('dir /b') DO @echo %%~fi pause ``` ##### 三、 %~dI - 仅将 %I 扩展到一个驱动器号 ``` @echo off FOR /F "delims==" %%i IN ('dir /b') DO @echo %%~di pause :: 仅输入磁盘目录 如 C: ``` ##### 四、 %~pI - 仅将 %I 扩展到一个路径 ``` @echo off FOR /F "delims==" %%i IN ('dir /b') DO @echo %%~pi pause :路径 \Users\Leon\Desktop\ ``` ##### 五、 %~nI - 仅将 %I 扩展到一个文件名 ``` @echo off FOR /F "delims==" %%i IN ('dir /b') DO @echo %%~ni pause :文件名无扩展后缀 ``` ##### 六、 %~xI - 仅将 %I 扩展到一个文件扩展名 只打印文件的扩展名 ##### 七、 %~sI - 扩展的路径只含有短名 打印绝对短文件名 ##### 八、 %~aI - 将 %I 扩展到文件的文件属性 打印文件的属性 ##### 九、 %~tI - 将 %I 扩展到文件的日期/时间 打印文件建立的日期 ##### 十、 %~zI - 将 %I 扩展到文件的大小 打印文件的大小 ##### 十一、 %~$PATH:I 在 PATH 变量里指定的路径里搜索 notepad.exe 文件,如果有 notepad.exe 则会把 他所在绝对路径打印出来,没有就打印一个错误! ``` @echo off FOR /F "delims=" %%i IN (“notepad.exe”) DO echo %%~$PATH:i pause ``` ### 0x04 批处理中的变量 ##### 一、系统变量 不需要赋值,调用而以 ``` %ALLUSERSPROFILE% 本地 返回“所有用户”配置文件的位置。 %APPDATA% 本地 返回默认情况下应用程序存储数据的位置。 %CD% 本地 返回当前目录字符串。 %CMDCMDLINE% 本地 返回用来启动当前的 Cmd.exe 的准确命令行。 %CMDEXTVERSION% 系统 返回当前的“命令处理程序扩展”的版本号。 %COMPUTERNAME% 系统 返回计算机的名称。 %COMSPEC% 系统 返回命令行解释器可执行程序的准确路径。 %DATE% 系统 返回当前日期。 %ERRORLEVEL% 系统 返回上一条命令的错误代码。通常用非零值表示错误。 %HOMEDRIVE% 系统 返回连接到用户主目录的本地工作站驱动器号。 %HOMEPATH% 系统 返回用户主目录的完整路径。 %LOGONSERVER% %NUMBER_OF_PROCESSORS% %OS% %PATH% %PATHEXT% 系统 返回操作系统认为可执行的文件扩展名的列表。 %PROCESSOR_ARCHITECTURE% 系统 返回处理器的芯片体系结构。 %PROCESSOR_IDENTFIER% 系统 返回处理器说明。 %PROCESSOR_LEVEL% 系统 返回计算机上安装的处理器的型号。 %PROCESSOR_REVISION% 系统 返回处理器的版本号。 %PROMPT% 本地 返回当前解释程序的命令提示符设置。由 Cmd.exe 生成。 %RANDOM% 系统 返回 0 到 32767 之间的任意十进制数字。由 Cmd.exe 生成。 %SYSTEMDRIVE% 系统 返回包含 Windows server operating system 根目录(即系统根目录) %SYSTEMROOT% 系统 返回 Windows server operating system 根目录的位置。 %TEMP% 和 %TMP% 系统和用户 返回对当前登录用户可用的应用程序所使用的默认临时目录。 %TIME% 系统 返回当前时间。 %USERDOMAIN% 本地 返回包含用户帐户的域的名称。 %USERNAME% 本地 返回当前登录的用户的名称。 %USERPROFILE% 本地 返回当前用户的配置文件的位置。 %WINDIR% 系统 返回操作系统目录的位置。 ``` %1 %2 %3 %4 %5第几个参数 ``` @echo off echo %1 %2 %3 %4 echo %1 echo %2 echo %3 echo %4 ::保存为test.BAT 进入 CMD 输入 test.bat 我是第一个参数 我是第二个参数 我是第三个参数 我是第四个参数 ``` %* 显示全部参数 %0 返回批处理所在绝对路径 或者无限循环执行 BAT ``` @echo off echo %0 pause @echo off net user %0 ``` ##### 二、自定义变量 由我们来给他赋予值的变量 ``` @echo off set var=我是值 echo %var% pause ``` 让用户手工输入变量的值 ``` @echo off set /p var=请输入变量的值 echo %var% pause ``` ### 0x05 set 命令详解 ##### 一、用 set 命令设置自定义变量 > 显示、设置或删除 cmd.exe 环境变量。 > > SET [variable=[string]] > > variable 指定环境变量名。 > string 指定要指派给变量的一系列字符串。 > > 要显示当前环境变量,键入不带参数的 SET。 > > 可仅用一个变量激活 SET 命令,等号或值不显示所有前缀匹配 > SET 命令已使用的名称的所有变量的值。例如: > > SET P > > 会显示所有以字母 P 打头的变量 > > 在 SET 命令中添加了两个新命令行开关: > > SET /A expression > SET /P variable=[promptString] ##### 二、用 set 命令进行简单计算 > /A 命令行开关指定等号右边的字符串为被评估的数字表达式。该表达式 > 评估器很简单并以递减的优先权顺序支持下列操作: > > () - 分组 > ! ~ - - 一元运算符 > * / % - 算数运算符 > + - - 算数运算符 > << >> - 逻辑移位 > & - 按位“与” > ^ - 按位“异” > | - 按位“或” > = *= /= %= += -= - 赋值 > &= ^= |= <<= >>= > , - 表达式分隔符 > 注意:DOS 计算只能精确到整数 ``` @echo off set /p input=请输入计算表达式: set /a var=%input% echo 计算结果:%input%=%var% pause ``` ``` @echo off set /p n=请输入 2 的几次方 : set /a num=1^<^<n echo %num% pause ``` ##### 三、用 set 命令进行字符串处理 ###### 1、字符串替换 > %PATH:str1=str2% 字符串变量%PATH%中的 str1 替换为 str2 ``` @echo off set a= bbs.verybat. cn echo 替换前的值 : "%a%" set var=%a: =% echo 替换后的值 : "%var%" pause ::输出bbs.verybat.cn ``` ###### 2、字符串截取 > 截取功能统一语法格式为:%a:~[m[,n]]% > > 方括号表示可选,%为变量标识符,a 为变量名,不可少,冒号用于分隔变量名和说明部分 , > 符号~可以简单理解为“偏移”即可,m 为偏移量(缺省为 0),n 为截取长度(缺省为全部) 跳过一个,取两个 ``` @echo off set a=bbs.verybat.cn set var=%a:~1,2% echo %var% pause ``` 取最后3个 ``` @echo off set a=bbs.verybat.cn set var=%a:~3% echo % ``` 从第几个开始, 不要最后几个 ``` @echo off set a=bbs.verybat.cn set var=%a:~2,-3% echo %var% pause ``` 求字符串单长度 ``` @echo off set /p str=请输入任意长度的字符串 : echo 你输入了字符串:"%str%" if not defined str(pause& goto :eof) set num=0 :len set /a num+=1 set str=%str:~0,-1% if defined str goto len echo 字符串长度为:%num% pause ``` ### 0x06 if 命令讲解 ##### 第一种用法:IF [NOT] ERRORLEVEL number command ``` @echo off net user IF %ERRORLEVEL% == 0 echo net user 执行成功了! pause ``` ##### 第二种用法:IF [NOT] string1==string2 command ``` @echo off set /p var=请输入第一个比较字符 : set /p var2=请输入第二个比较字符 : if %var% == %var2% (echo 我们相等) ELSE echo 我们不相等 pause ``` 上面这个例子 判断你输入的值是不是相等,但是你如果输入相同的字符,但是如果其中一个后面打了一个空格, 这个例子还是会认为相等,如何让有空格的输入不相等呢?我们在比较字符上加个双引号 ``` @echo off set /p var=请输入第一个比较字符 : set /p var2=请输入第二个比较字符(多输入个空格试试 ): if "%var%" == "%var2%" (echo 我们相等) ELSE echo 我们不相等 pause ``` ##### 第三种用法:IF [NOT] EXISTfilename command 判断某个文件或者文件夹是否存在 ``` @echo off if exist "c:\test" (echo 存在文件) ELSE echo 不存在文件 pause ``` ##### 第四种用法:IF 增强的用法 > IF [/I]string1 compare-op string2 command > IF CMDEXTVERSION number command > IF DEFINED variable command 后面两个用法,跟前面的类似,不做介绍, ``` @echo off if a == A (echo 我们相等) ELSE echo 我们不相等 pause ``` 不相等 ``` @echo off if /i a == A (echo 我们相等) ELSE echo 我们不相等 pause ``` 加上/I 不区分大小 结果 相等 **判断数字的符号** > EQU - 等于 > NEQ - 不等于 > LSS - 小于 > LEQ - 小于或等于 > GTR- 大于 > GEQ - 大于或等于 ``` @echo off set /p var=请输入一个数字 : if %var% LEQ 4 (echo 我小于等于 4) ELSE echo 我不小于等于 4 pause ``` ### 0x07 DOS 编程高级技巧 ##### 一、界面设计 ``` @echo off cls title 终极多功能修复 :menu cls color 0A echo. echo ============================== echo 请选择要进行的操作,然后按回车 echo ============================== echo. echo 1.网络修复及上网相关设置,修复 IE,自定义屏蔽网站 echo. echo 2.病毒专杀工具,端口关闭工具,关闭自动播放 echo. echo 3.清除所有多余的自启动项目,修复系统错误 echo. echo 4.清理系统垃圾,提高启动速度 echo. echo Q.退出 echo. echo. :cho set choice= set /p choice= 请选择 : IF NOT "%choice%"=="" SET choice=%choice:~0,1% if /i "%choice%"=="1" goto ip if /i "%choice%"=="2" goto setsave if /i "%choice%"=="3" goto kaiji if /i "%choice%"=="4" goto clean if /i "%choice%"=="Q" goto endd echo 选择无效,请重新输入 echo. goto cho :ip echo ip pause goto :menu :setsave echo setsave pause goto :menu :kaiji echo kaiji pause goto :menu :clean echo clean pause goto :menu :endd exit ``` ##### 二、 if …else…条件语句 ``` IF EXIST filename ( del filename ) ELSE ( echo filename missing ) ``` ##### 三、循环语句 > FOR /L%variable IN (start,step,end) DO command [command-parameters] > > FOR %%variable IN (set) DO command [command-parameters] > > FOR /R[[drive:]path] %variable IN (set) DO command [command-parameters] ``` @echo off set var=0 rem ************循环开始了 :continue set /a var+=1 echo 第%var%此循环 if %var% lss 100 goto continue rem ************循环结束了 echo 循环执行完毕 pause ``` ##### 四、子程序 > CALL:label arguments > :label > command1 > command2 > ... > commandn > goto :eof ``` @echo off call :sub return 你好 echo 子程序返回值:%return% pause :sub set %1=%2 goto :eof 运行结果:你好 ``` 设计一个求多个整数相加的子程序 ``` @echo off set sum=0 call :sub sum 10 20 35 echo 数据求和结果:%sum% pause :sub rem 参数 1 为返回变量名称 set /a %1=%1+%2 shift /2 if not "%2"=="" goto sub goto :eof ``` ##### 五、用 ftp 命令实现自动下载 ``` ftp #进入ftp open 90.52.8.3 #打开 ip user iware #用户为 iware password8848 #密码 bin #二进制传输模式 prompt cd tmp1 #切换至 iware 用户下的 tmp1 目录 pwd lcd d:\download #本地目录 mget * #下载 tmp1 目录下的所有文件 bye #退出 ftp ``` ##### 六、调用 VBScript 程序 > 用法:CScript scriptname.extension [option...] [arguments...] > > 选项: > //B 批模式:不显示脚本错误及提示信息 > //D 启用 Active Debugging > //E:engine 使用执行脚本的引擎 > //H:CScript 将默认的脚本宿主改为 CScript.exe > //H:WScript 将默认的脚本宿主改为 WScript.exe (默认) > //I 交互模式(默认,与 //B 相对) > //Job:xxxx 执行一个 WSF 工作 > //Logo 显示徽标(默认) > //Nologo 不显示徽标:执行时不显示标志 > //S 为该用户保存当前命令行选项 > //T:nn 超时设定秒:允许脚本运行的最长时间 > //X 在调试器中执行脚本 > //U 用 Unicode 表示来自控制台的重定向 I/O ##### 七、将批处理转化为可执行文件 BAT2EXE第三方工具 > 在 DOS 环境下,可执行文件的优先级由高到低依次为.com>.exe>.bat>.cmd ##### 八、时间延迟 ``` @echo off chcp 65001 echo 延时前!%time% ping /n 5 127.0.0.1 >nul echo 延时后!%time% pause echo 延时前!%time% for /l %%i in (1,1,5000) do echo %%i>nul echo 延时后!%time% pause ``` ##### 九、模拟进度条 ``` @echo off mode con cols=113 lines=15 &color 9f chcp 65001 cls echo. echo 程序正在初始化. . . echo. echo ┌──────────────────────────────────────┐ set /p a=■<nul for /L %%i in (1 1 38) do set /p a=■<nul&ping /n 1 127.0.0.1>nul echo 100%% echo └──────────────────────────────────────┘ pause ```
作者:じ☆ve不哭
## 特殊字符 ### # 行首以#(#!是个例外)开头是注释 ,也可以放在于本行命令的后边. ``` echo "The # here does not begin a comment." echo 'The # here does not begin a comment.' echo The \# here does not begin a comment. echo The # 这里开始一个注释. echo ${PATH#*:} # 参数替换, 不是一个注释. echo $(( 2#101011 )) # 数制转换, 不是一个注释. # 感谢, S.C. ``` ### ; 命令分隔符[分号, 即;]. 可以在同一行上写两个或两个以上的命令. ``` echo hello; echo there if [ -x "$filename" ]; then # 注意: "if"和"then"需要分隔. # 为什么? echo "File $filename exists."; cp $filename $filename.bak else echo "File $filename not found."; touch $filename fi; echo "File test complete." ``` ### ;; 终止case选项[双分号, 即;;] ``` case "$variable" in abc) echo "\$variable = abc" ;; xyz) echo "\$variable = xyz" ;; esac ``` ### . "点"命令[句点, 即.]. 等价于source命令 "点"作为文件名的一部分. 如果点放在文件名的开头的话, 那么这个文件将会成为"隐藏"文件, 并且ls命令将不会正常的显示出这个文件. ``` touch .hidden ls ls -a ``` 如果作为目录名的话, 一个单独的点 代表当前的工作目录, 而 两个点 表示上一级目录. ``` cd . cd .. ``` 点 经常会出现在文件移动命令的目的参数(目录)的位置上. ``` cp /home/bozo/current_work/junk/* . ``` "点"字符匹配. 当用作匹配字符的作用时, 通常都是作为正则表达式的一部分来使用, "点"用来 匹配任何的单个字符. ### " 部分引用[双引号, 即"]. "STRING" 将会阻止(解释) STRING 中大部分特殊的字符 ### ' 全引用[单引号, 即']. 'STRING' 将会阻止 STRING 中所有特殊字符的解释. 这是一种比使用"更强 烈的形式. ### , 逗号操作符. 逗号操作符链接了一系列的算术操作. 虽然里边所有的内容都被运行了,但只有最后 一项被返回. ``` et "t2 = ((a = 9, 15 / 3))" # Set "a = 9" and "t2 = 15 / 3" ``` ### \ 转义符[反斜线, 即\]. 一种对单字符的引用机制. \X 将会"转义"字符 X . 这等价于 "X" , 也等价于 'X' . \通常用来转义"和', 这样双引号和但引号就 不会被解释成特殊含义了. ### / 文件名路径分隔符[斜线, 即/]. 分隔文件名不同的部分(比如 /home/bozo/projects/Makefile ). 也可以用来作为除法算术操作符. ### ` 替换. `command`结构可以将命令的输出赋值到一个变量中去. 我们在后边的后置引用 (backquotes)或后置标记(backticks)中也会讲解. ### : 空命令[冒号, 即:]. 等价于"NOP" ( no op , 一个什么也不干的命令). 也可以被认为与shell的 内建命令true作用相同. ":"命令是一个bash的内建命令, 它的退出码(exit status)是"true"(0). 在if/then中的占位符: ``` if condition then : # 什么都不做,引出分支. else take-some-action fi ``` 在与>重定向操作符结合使用时, 将会把一个文件清空, 但是并不会修改这个文件的权限. 如果之 前这个文件并不存在, 那么就创建这个文件. ``` : > data.xxx # 与 cat /dev/null >data.xxx 的作用相同 # 然而,这并不会产生一个新的进程, 因为":"是一个内建命令. ``` 也可能用来作为注释行, 虽然我们不推荐这么做. 使用#来注释的话, 将关闭剩余行的错误检查, 所以可以在注释行中写任何东西. 然而, 使用:的话将不会这样. ``` : This is a comment that generates an error, ( if [ $x -eq 3] ). ``` ":"还用来在 /etc/passwd 和$PATH变量中做分隔符. ``` echo $PATH /usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/sbin:/usr/sbin:/usr/games ``` ### ! 取反操作符[叹号, 即!]. !操作符将会反转命令的退出码的结果, (具体参见例子 6-2). 也会反 转测试操作符的意义, 比如修改"等号"( = )为"不等号"( != ). !操作符是Bash的关键字. 在一个不同的上下文中, !也会出现在变量的间接引用中. ### * 通配符. *可以用来做文件名匹配(这个东西有个专有名词叫globbing)的"通配符". 含义是, 可以用来匹配给定目录下的任何文件名. 算术操作符. 在算术操作符的上下文中, *号表示乘法运算. 如果要做求幂运算, 使用**, 这是求幂操作符. ### ? 测试操作符. 在一个特定的表达式中, ?用来测试一个条件的结果. 在一个双括号结构中, ?就是C语言的三元操作符. 参见例子 9-31. 在参数替换表达式中, ?用来测试一个变量是否被set了. . 通配符. ?在通配(globbing)中, 用来做匹配单个字符的"通配符", 在正则表达式中, 也是用 来表示一个字符. ### $ 变量替换(引用变量的内容). ``` var1=5 var2=23skidoo echo $var1 # 5 echo $var2 # 23skidoo ``` 在一个变量前面加上$用来引用这个变量的 值 行结束符. 在正则表达式中, "$"表示行结束符. ### ${} 参数替换. #### \$*, \$@ 位置参数. #### \$? 退出状态码变量. \$? 变量 保存了一个命令, 一个函数, 或者是脚本本身的退出状态码. #### \$\$ 进程ID变量. 这个$$ 变量 保存了它所在脚本的 进程 ID ### () 命令组. ``` (a=hello; echo $a) a=123 ( a=321; ) echo "a = $a" # a = 123 # 在圆括号中a变量, 更像是一个局部变量. ``` 初始化数组. ``` Array=(element1 element2 element3) ``` ### {xxx,yyy,zzz,...} 大括号扩展. ``` cat {file1,file2,file3} > combined_file # 把file1, file2, file3连接在一起, 并且重定向到combined_file中. cp file22.{txt,backup} # 拷贝"file22.txt"到"file22.backup"中 ``` 一个命令可能会对 大括号 [2] 中的以逗号分割的文件列表起作用. (通配(globbing))将对大括号 中的文件名做扩展. 在大括号中, 不允许有空白, 除非 这个空白被引用或转义. ``` echo {file1,file2}\ :{\ A," B",' C'} #file1 : A file1 : B file1 : C file2 : A file2 : B file2 : C ``` {} 代码块. 又称内部组 , 这个结构事实上创建了一个 匿名函数. 然而, 与"标准"函数不同的是, 在其中声明的变量,对于脚本其他部分的代码来说还是**可见**的. ``` a=123 { a=321; } echo "a = $a" # a = 321 (说明在代码块中对变量a所作的修改, 影响了外边的变量) ``` 代码块和I/O重定向 ``` #!/bin/bash # 从/etc/fstab中读行. File=/etc/fstab { read line1 read line2 } < $File echo "First line in $File is:" echo "$line1" echo echo "Second line in $File is:" echo "$line2" exit 0 ``` ### {} \; 路径名. 一般都在find命令中使用. 这 不是 一个shell内建命令. ### [ ] 条件测试. 条件测试表达式放在[ ]中. 值得注意的是[是shell内建test命令的一部分, 并不 是 /usr/bin/test 中的外部命令的一个链接. 数组元素. 在一个array结构的上下文中, 中括号用来引用数组中每个元素的编号. ``` Array[1]=slot_1 echo ${Array[1]} ``` 字符范围.用作正则表达式的一部分, 方括号描述一个匹配的字符范围. ### [[ ]] 测试. 测试表达式放在[[ ]]中 ### (( )) 整数扩展. 扩展并计算在(( ))中的整数表达式. ### > &> >& >> < <> 重定向. scriptname >filename 重定向 scriptname 的输出到文件 filename 中. 如果 filename 存在的话, 那 么将会被覆盖. command &>filename 重定向 command 的 stdout 和 stderr 到 filename 中. command >&2 重定向 command 的 stdout 到 stderr 中. scriptname >>filename 把 scriptname 的输出追加到文件 filename 中. 如果 filename 不存在的话, 将会被创建. [i]<>filename 打开文件 filename 用来读写, 并且分配文件描述符i给这个文件. 如果 filename 不 存在, 这个文件将会被创建. 进程替换. (command)> <(command) 在一种不同的上下文中, "<"和">"可用来做 字符串比较操作. 在另一种上下文中, "<"和">"可用来做 整数比较操作. ### << 用在here document中的重定向. ### <<< 用在here string中的重定向. ### <, > ASCII comparison. ``` veg1=carrots veg2=tomatoes if [[ "$veg1" < "$veg2" ]] then echo "Although $veg1 precede $veg2 in the dictionary," echo "this implies nothing about my culinary preferences." else echo "What kind of dictionary are you using, anyhow?" fi ``` ### \<, \> 正则表达式中的单词边界 . ``` grep '\<the\>' textfile ``` ### | 管道. 分析前边命令的输出, 并将输出作为后边命令的输入. 这是一种产生命令链的好方法. ``` echo ls -l | s # 传递"echo ls -l"的输出到shell中, #+ 与一个简单的"ls -l"结果相同. ``` > 管道是进程间通讯的一个典型办法, 将一个进程的 stdout 放到另一个进程的 stdin 中. 标 > 准的方法是将一个一般命令的输出, 比如cat或者echo, 传递到一个 "过滤命令"(在这个 > 过滤命令中将处理输入)中, 然后得到结果. > cat \$filename1 \$filename2 | grep \$search_word ### \>| 强制重定向(即使设置了noclobber选项 -- 就是-C选项). 这将强制的覆盖一个现存文件. ### || 或-逻辑操作. 在一个条件测试结构中, 如果条件测试结构两边中的 任意一边 结果为true的话, ||操作就会返回0(代表执行成功). ### & 后台运行命令. 一个命令后边跟一个& 表示在后台运行. ``` #!/bin/bash # background-loop.sh for i in 1 2 3 4 5 6 7 8 9 10 # 第一个循环. do echo -n "$i " done & # 在后台运行这个循环. # 在第2个循环之后, 将在某些时候执行. echo # 这个'echo'某些时候将不会显示. for i in 11 12 13 14 15 16 17 18 19 20 # 第二个循环. do echo -n "$i " done echo # 这个'echo'某些时候将不会显示. # ====================================================== # 期望的输出应该是: # 1 2 3 4 5 6 7 8 9 10 # 11 12 13 14 15 16 17 18 19 20 # 然而实际的结果有可能是: 11 12 13 14 15 16 17 18 19 20 # 1 2 3 4 5 6 7 8 9 10 bozo $ # (第2个'echo'没执行, 为什么?) # 也可能是: # 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # (第1个'echo'没执行, 为什么?) # 非常少见的执行结果, 也有可能是: # 11 12 13 1 2 3 4 5 6 7 8 9 10 14 15 16 17 18 19 20 # 前台的循环先于后台的执行. exit 0 # Nasimuddin Ansari 建议加一句 sleep 1 #+ 在6行和14行的 echo -n "$i" 之后加这句. #+ 为了真正的乐趣. ``` ### && 与-逻辑操作. 在一个条件测试结构中, 只有在条件测试结构的 两边 结果都为true的时候, &&操作 才会返回0(代表sucess). ### - 选项, 前缀. 在所有的命令内如果想使用选项参数的话,前边都要加上"-". > COMMAND -[Option1]\[Option2][...] > ls -al > sort -dfu \$filename > set -- \$variable ``` if [ $file1 -ot $file2 ] then echo "File $file1 is older than $file2." fi if [ "$a" -eq "$b" ] then echo "$a is equal to $b." fi if [ "$c" -eq 24 -a "$d" -eq 47 ] then echo "$c equals 24 and $d equals 47." fi ``` 先前的工作目录. cd -将会回到先前的工作目录. 它使用了$OLDPWD 环境变量 ``` cd - ``` 减号. 减号属于算术操作. ### = 等号. 赋值操作 ``` a=28 echo $a # 28 ``` ### + 加号. 加法算术操作. 在另一种上下文环境中, +也是一种正则表达式操作. 选项. 一个命令或者过滤器的选项标记. 某些命令内建命令使用+来打开特定的选项, 用-来禁用这些特定的选项. ### % 取模. 取模(一次除法的余数)算术操作. 在不同的上下文中, %也是一种模式匹配操作. ### ~ home目录. 相当于$HOME内部变量. ~leon 是leon的home目录, 并且ls ~leon将列出其中的内容. ~/就是当前用户的home目录, 并且ls ~/将列出其中的内容. ``` # echo ~leon /home/leon ``` ### ~+ 当前工作目录. 相当于\$PWD内部变量. ~- 先前的工作目录. 相当于\$OLDPWD内部变量. ### =~ 正则表达式匹配. 这个操作将会在version 3版本的Bash部分进行讲解. ### ^ 行首. 在正则表达式中, "^"表示定位到文本行的行首. ### 控制字符 #### Ctl-B 退格(非破坏性的), 就是退格但是不删掉前面的字符. #### Ctl-C break. 终结一个前台作业 #### Ctl-D 在console或者在 xterm 窗口中输入的时候, Ctl-D 将删除光标下字符. 当没有字符时, Ctl- D 将退出当前会话, 在一个xterm窗口中, 则会产生关闭此窗口的效果 #### Ctl-G "哔" (beep). 在一些老式的打字机终端上, 它会响一下铃. #### Ctl-H "退格"(破坏性的), 就是在退格之后, 还要删掉前边的字符. ``` #!/bin/bash # Embedding Ctl-H in a string. a="^H^H" # 两个 Ctl-H's (backspaces). echo "abcdef" # abcdef echo -n "abcdef$a " # abcd f # Space at end ^ ^ 两次退格. echo -n "abcdef$a" # abcdef # 结尾没有空格 没有 backspace 的效果了(why?). # 结果并不像期望的那样. echo; echo ``` #### Ctl-I 水平制表符. #### Ctl-J 重起一行(换一行并到行首). 在脚本中, 也可以使用8进制表示法 -- '\012' 或者16进制 表示法 -- '\x0a' 来表示. #### Ctl-K 垂直制表符. 当在console或者 xterm 窗口中输入文本时, Ctl-K 将会删除从光标所在处到行为的全部字 符. 在脚本中, Ctl-K 的行为有些不同, 具体请参见下边的Lee Maschmeyer的例子程序. #### Ctl-L 清屏(清除终端的屏幕显示). 在终端中, 与clear命令的效果相同. 当发送到打印机上时, Ctl-L 会让打印机将打印纸卷到最后. #### Ctl-M 回车. #### Ctl-Q 恢复(XON). 在一个终端中恢复 stdin . #### Ctl-S 挂起(XOFF). 在一个终端中冻结 stdin . (使用Ctl-Q可以恢复输入.) #### Ctl-U 删除光标到行首的所有字符. 在某些设置下, 不管光标的所在位置 Ctl-U 都将删除整行输 入. #### Ctl-V 当输入字符时, Ctl-V 允许插入控制字符. 比如, 下边的两个例子是等价的: 1 echo -e '\x0a' 2 echo <Ctl-V><Ctl-J> Ctl-V 主要用于文本编辑. #### Ctl-W 当在控制台或一个xterm窗口敲入文本时, Ctl-W 将会删除当前光标到左边最近一个空格间 的全部字符. 在某些设置下, Ctl-W 将会删除当前光标到左边第一个非字母或数字之间的全 部字符. #### Ctl-Z 暂停前台作业. ### 空白 用来分隔函数, 命令或变量. . 空白包含 空格 , tab , 空行 , 或者是它们之间任意的组合体. [4] 在某些上下文中, 比如变量赋值, 空白是不被允许的, 会产生语法错误. 空行不会影响脚本的行为, 因此使用空行可以很好的划分独立的函数段以增加可读性. 特殊变量$IFS用来做一些输入命令的分隔符, 默认情况下是空白. 如果想在字符串或变量中使用空白, 那么应该使用引用. ## 变量和参数 ### 变量替换 变量的 名字 就是变量保存 值 的地方. 引用变量的值就叫做 变量替换 #### ``` #!/bin/bash # 变量赋值和替换 a=375 hello=$a #------------------------------------------------------------------ # 强烈注意, 在赋值的的时候, 等号前后一定不要有空格. # 如果出现空格会怎么样? # "VARIABLE =value" # ^ #% 脚本将尝试运行一个"VARIABLE"的命令, 带着一个"=value"参数. # "VARIABLE= value" # ^ #% 脚本将尝试运行一个"value"的命令, #+ 并且带着一个被赋值成""的环境变量"VARIABLE". #------------------------------------------------------------------ ------- echo hello # 没有变量引用, 只是个hello字符串. echo $hello echo ${hello} # 同上. echo "$hello" echo "${hello}" echo hello="A B C D" echo $hello # A B C D echo "$hello" # A B C D # 就象你看到的echo $hello 和 echo "$hello" 将给出不同的结果. # =============================================================== # 引用一个变量将保留其中的空白, 当然, 如果是变量替换就不会保留了. # =============================================================== echo echo '$hello' # $hello # ^ ^ # 全引用的作用将会导致"$"被解释为单独的字符, #+ 而不是变量前缀. # 注意这两种引用所产生的不同的效果. hello= # 设置为空值. echo "\$hello (null value) = $hello" # 注意设置一个变量为null, 与unset这个变量, 并不是一回事 #+ 虽然最终的结果相同(具体见下边). # -------------------------------------------------------------- # 可以在同一行上设置多个变量, #+ 但是必须以空白进行分隔. # 慎用, 这么做会降低可读性, 并且不可移植. var1=21 var2=22 var3=$V3 echo echo "var1=$var1 var2=$var2 var3=$var3" # 在老版本的"sh"上可能会引起问题. # -------------------------------------------------------------- echo; echo numbers="one two three" # ^ ^ other_numbers="1 2 3" # ^ ^ # 如果在变量中存在空白, If there is whitespace embedded within a variable, #+ 那么就必须加上引用. # other_numbers=1 2 3 # 给出一个错误消息. echo "numbers = $numbers" echo "other_numbers = $other_numbers" # other_numbers = 1 2 3 # 不过也可以采用将空白转义的方法. mixed_bag=2\ ---\ Whatever # ^ ^ 在转义符后边的空格(\). echo "$mixed_bag" # 2 --- Whatever echo; echo echo "uninitialized_variable = $uninitialized_variable" # Uninitialized变量为null(就是没有值). uninitialized_variable= # 声明, 但是没有初始化这个变量, #+ 其实和前边设置为空值的作用是一样的. echo "uninitialized_variable = $uninitialized_variable" # 还是一个空值. uninitialized_variable=23 # 赋值. unset uninitialized_variable # Unset这个变量. echo "uninitialized_variable = $uninitialized_variable" # 还是一个空值. echo exit 0 ``` ### 变量赋值 #### = 赋值操作( 前后都不能有空白 ) ``` #!/bin/bash # "裸体"变量 echo # 变量什么时候是"裸体"的, 比如前边少了$的时候? # 当它被赋值的时候, 而不是被引用的时候. # 赋值 a=879 echo "The value of \"a\" is $a." # 使用'let'赋值 let a=16+5 echo "The value of \"a\" is now $a." echo # 在'for'循环中(事实上, 这是一种伪赋值): echo -n "Values of \"a\" in the loop are: " for a in 7 8 9 11 do echo -n "$a " done echo echo # 使用'read'命令进行赋值(这也是一种赋值的类型): echo -n "Enter \"a\" " read a echo "The value of \"a\" is now $a." echo exit 0 ``` ``` #!/bin/bash a=23 # 简单的赋值 echo $a b=$a echo $b # 现在让我们来点小变化(命令替换). a=`echo Hello!` # 把'echo'命令的结果传给变量'a' echo $a # 注意, 如果在一个#+的命令替换结构中包含一个(!)的话, #+ 那么在命令行下将无法工作. #+ 因为这触发了Bash的"历史机制." # 但是, 在脚本中使用的话, 历史功能是被禁用的, 所以就能够正常的运行. a=`ls -l` # 把'ls -l'的结果赋值给'a' echo $a # 然而, 如果没有引号的话将会删除ls结果中多余的tab和换行符. echo echo "$a" # 如果加上引号的话, 那么就会保留ls结果中的空白符. # (具体请参阅"引用"的相关章节.) exit 0 ``` 使用 $(...) 机制来进行变量赋值(这是一种比后置引用(反引号`)更新的一种方法). ``` R=$(cat /etc/redhat-release) arch=$(uname -m) ``` ### Bash变量是不区分类型的 本质上, Bash变量都是字符串. 但是依赖于具体 的上下文, Bash也允许比较操作和整数操作. 其中的关键因素就是, 变量中的值是否只有数字. ``` #!/bin/bash # int-or-string.sh: 整型还是字符串? a=2334 # 整型. let "a += 1" echo "a = $a " # a = 2335 echo # 还是整型. b=${a/23/BB} # 将"23"替换成"BB". # 这将把变量b从整型变为字符串. echo "b = $b" # b = BB35 declare -i b # 即使使用declare命令也不会对此有任何帮助. echo "b = $b" # b = BB35 let "b += 1" # BB35 + 1 = echo "b = $b" # b = 1 echo c=BB34 echo "c = $c" # c = BB34 d=${c/BB/23} # 将"BB"替换成"23". # 这使得变量$d变为一个整形. echo "d = $d" # d = 2334 let "d += 1" # 2334 + 1 = echo "d = $d" # d = 2335 echo # null变量会如何呢? e="" echo "e = $e" # e = let "e += 1" # 算术操作允许一个null变量? echo "e = $e" # e = 1 echo # null变量将被转换成一个整型变量. # 如果没有声明变量会怎样? echo "f = $f" # f = let "f += 1" # 算术操作能通过么? echo "f = $f" # f = 1 echo # 未声明的变量将转换成一个整型变量. # 所以说Bash中的变量都是不区分类型的. exit 0 ``` ### 特殊的变量类型 #### 局部变量 这种变量只有在代码块或者函数中(参见函数中的局部变量)才可见. #### 环境变量 这种变量将影响用户接口和shell的行为 如果一个脚本要设置一个环境变量, 那么需要将这些变量"export"出来, 也就是需要通知到脚本 本地的环境. 这是export命令的功能. > 一个脚本只能够export变量到这个脚本所产生的子进程, 也就是说只能够对 > 这个脚本所产生的命令和进程起作用. 如果脚本是从命令行中调用的, 那么 > 这个脚本所export的变量是 不能 影响命令行环境的. 也就是说, 子进程是不 > 能够export变量来影响产生自己的父进程的环境的. #### 位置参数 从命令行传递到脚本的参数: \$0 , \$1 , \$2 , \$3 . . . $0 就是脚本文件自身的名字, \$1 是第一个参数, \$2 是第二个参数, \$3 是第三个参数, 然后是第 四个. \$9 之后的位置参数就必须用大括号括起来了, 比如, \${10} , \${11} , \${12} . 两个比较特殊的变量\$*和\$@ 表示 所有的 位置参数. ``` #!/bin/bash # 作为用例, 调用这个脚本至少需要10个参数, 比如: # ./scriptname 1 2 3 4 5 6 7 8 9 10 MINPARAMS=10 echo echo "The name of this script is \"$0\"." # 添加./是表示当前目录 echo "The name of this script is \"`basename $0`\"." # 去掉路径名, 剩下文件名, (参见'basename') echo if [ -n "$1" ] # 测试变量被引用. then echo "Parameter #1 is $1" # 需要引用才能够转义"#" fi if [ -n "$2" ] then echo "Parameter #2 is $2" fi if [ -n "$3" ] then echo "Parameter #3 is $3" fi # ... if [ -n "${10}" ] # 大于$9的参数必须用{}括起来. then echo "Parameter #10 is ${10}" fi echo "-----------------------------------" echo "All the command-line parameters are: "$*"" if [ $# -lt "$MINPARAMS" ] then echo echo "This script needs at least $MINPARAMS command-linearguments!" fi echo exit 0 ``` > 如果脚本需要一个命令行参数, 而在调用的时候, 这个参数没被提供, 那么 > 这就可能造成给这个参数赋一个null变量, 通常情况下, 这都会产生问题. > 一种解决这个问题的办法就是使用添加额外字符的方法, 在使用这个位置参 > 数的变量和位置参数本身的后边全部添加同样的额外字符 ``` variable1_=$1_ # 而不是 variable1=$1 # 这将阻止报错, 即使在调用时没提供这个位置参数. 3 critical_argument01=$variable1_ # 这个扩展的字符是可以被消除掉的, 就像这样. variable1=${variable1_/_/} # 副作用就是$variable1_多了一个下划线. # 这里使用了参数替换模版的一种形式(后边会有具体的讨论). # (在一个删除动作中, 节省了一个替换模式.) # 处理这个问题的一个更简单的办法就是 #+ 判断一下这个位置参数是否传递下来了. if [ -z $1 ] then exit $E_MISSING_POS_PARAM fi # 然而, 象Fabian Kreutz所指出的那样,上边的方法将可能产生一个意外的副作用. # 参数替换才是更好的方法:${1:-$DefaultVal} # 具体参见"参数替换"的相关章节在"变量重游"那章. ``` shift命令 ``` #!/bin/bash # 使用'shift'来逐步存取所有的位置参数. # 给脚本命个名, 比如shift, #+ 然后给脚本传递一些位置参数, 比如: # ./shft a b c def 23 skidoo until [ -z "$1" ] # 直到所有的位置参数都被存取完... do echo -n "$1 " shift done echo # 额外的换行. exit 0 ``` ## 引用 > 引用的字面意思就是将字符串用双引号括起来. 它的作用就是保护字符串中的特殊字符不被shell或者 > shell脚本重新解释, 或者扩展. ``` grep '[Ff]irst' *.txt file1.txt:This is the first line of file1.txt. file2.txt:This is the First line of file2.txt. ``` echo 换行 ``` bash$ echo $(ls -l) total 8 -rw-rw-r-- 1 bozo bozo 130 Aug 21 12:57 t222.sh -rw-rw-r-- 1 bozo bozo 78 Aug 21 12:57 t71.sh bash$ echo "$(ls -l)" total 8 -rw-rw-r-- 1 bozo bozo 130 Aug 21 12:57 t222.sh -rw-rw-r-- 1 bozo bozo 78 Aug 21 12:57 t71.sh ``` ### 引用变量 > 在一个双引号中通过直接使用变量名的方法来引用变量 ``` variable1="a variable containing five words" COMMAND This is $variable1 # 用下面7个参数执行COMMAND命令: # "This" "is" "a" "variable" "containing" "five" "words" COMMAND "This is $variable1" # 用下面1个参数执行COMMAND命令: # "This is a variable containing five words" variable2="" # Empty. COMMAND $variable2 $variable2 $variable2 # COMMAND将不带参数执行. COMMAND "$variable2" "$variable2" "$variable2" # COMMAND将以3个空参数来执行. COMMAND "$variable2 $variable2 $variable2" # COMMAND将以1个参数来执行(2空格). ``` > 单引号(' ')操作与双引号基本一样, 但是不允许引用变量, 因为$的特殊意义被关闭了. 在单引号中, > 任何 特殊字符都按照字面的意思进行解释, 除了'. 所以说单引号("全引用")是一种比双引号("部分引 > 用")更严格的引用方法. ``` #!/bin/bash # weirdvars.sh: echo出一些诡异变量. var="'(]\\{}\$\"" echo $var # '(]\{}$" echo "$var" # '(]\{}$" 和上一句没什么区别.Doesn't make a difference. echo IFS='\' echo $var # '(] {}$" \ 字符被空白符替换了, 为什么? echo "$var" # '(]\{}$" # 这个例子由Stephane Chazelas提供. exit 0 ``` ### 转义 > 转义 是一种引用单个字符的方法. 一个前面放上转义符 (\)的字符就是告诉shell这个字符按照字面的 > 意思进行解释, 换句话说, 就是这个字符失去了它的特殊含义. ``` #!/bin/bash # escaped.sh: 转义符 echo; echo echo "\v\v\v\v" # 逐字的打印\v\v\v\v. # 使用-e选项的'echo'命令来打印转义符. echo "=============" echo "VERTICAL TABS" echo -e "\v\v\v\v" # 打印4个垂直制表符. echo "==============" echo "QUOTATION MARK" echo -e "\042" # 打印" (引号, 8进制的ASCII 码就是42). echo "==============" # 如果使用$'\X'结构,那-e选项就不必要了. echo; echo "NEWLINE AND BEEP" echo $'\n' # 新行. echo $'\a' # 警告(蜂鸣). echo "===============" echo "QUOTATION MARKS" # 版本2以后Bash允许使用$'\nnn'结构. # 注意在这里, '\nnn\'是8进制的值. echo $'\t \042 \t' # 被水平制表符括起来的引号("). # 当然,也可以使用16进制的值,使用$'\xhhh' 结构. echo $'\t \x22 \t' # 被水平制表符括起来的引号("). # 感谢, Greg Keraunen, 指出了这点. # 早一点的Bash版本允许'\x022'这种形式. echo "===============" echo # 分配ASCII字符到变量中. # ---------------------------------------- quote=$'\042' # " 被赋值到变量中. echo "$quote This is a quoted string, $quote and this lies outside the quotes." echo # 变量中的连续的ASCII字符. triple_underline=$'\137\137\137' # 137是八进制的'_'. echo "$triple_underline UNDERLINE $triple_underline" echo ABC=$'\101\102\103\010' # 101, 102, 103是八进制码的A, B, C. echo $ABC echo; echo escape=$'\033' # 033 是八进制码的esc. echo "\"escape\" echoes as $escape" # 没有变量被输出. echo; echo exit 0 ``` ## 退出和退出状态码 > exit 被用来结束一个脚本, 就像在 C 语言中一样. 它也返回一个值, 并且这个值会传递给脚本的父进 > 程, 父进程会使用这个值做下一步的处理. > > 不带参数的exit命令与 exit $?的效果是一样的, 甚至脚本的结尾不写exit, 也与前两者 > 的效果相同. > > \$? 保存了最后所执行的命令的退出状态码. 当函数返回之后, \$? 保存函数中最后所执行的命令的退出状 > 态码. 这就是bash对函数"返回值"的处理方法. 当一个脚本退出, \$? 保存了脚本的退出状态码, 这个退 > 出状态码也就是脚本中最后一个执行命令的退出状态码. 一般情况下, 0 表示成功, 在范围1 - 255的整 > 数表示错误. ``` #!/bin/bash echo hello echo $? # 退出状态为0, 因为命令执行成功. lskdf # 无效命令. echo $? # 非零的退出状态, 因为命令执行失败. echo exit 113 # 返回113退出状态给shell. # 为了验证这个结果, 可以在脚本结束的地方使用"echo $?". # 一般的, 'exit 0' 表示成功, #+ 而一个非零的退出码表示一个错误, 或者是反常的条件. ``` ## 条件判断 p76 ### 条件测试结构 > - if/then结构用来判断命令列表的退出状态码是否为0(因为在UNIX惯例, 0表示"成功"), 如果成 > 功的话, 那么就执行接下来的一个或多个命令. > - 有一个专有命令[ (左中括号, 特殊字符). 这个命令与test命令等价, 并且出于效率上的考虑, > 这是一个内建命令. 这个命令把它的参数作为比较表达式或者作为文件测试, 并且根据比较的结 > 果来返回一个退出状态码(0 表示真, 1表示假). > - 在版本2.02的Bash中, 引入了[[ ... ]] 扩展测试命令 , 因为这种表现形式可能对某些语言的程序 > 员来说更容易熟悉一些. 注意[[是一个关键字, 并不是一个命令. > Bash把 [[ \$a -lt \$b ]] 看作一个单独的元素, 并且返回一个退出状态码. > (( ... ))和let ...结构也能够返回退出状态码, 当它们所测试的算术表达式的结果为非零的时 > 候, 将会返回退出状态码0. 这些算术扩展结构被用来做算术比较. ``` if cmp a b &> /dev/null # 禁止输出. then echo "Files a and b are identical." else echo "Files a and b differ." fi # 非常有用的"if-grep"结构: # ------------------------ if grep -q Bash file then echo "File contains at least one occurrence of Bash." fi word=Linux letter_sequence=inu if echo "$word" | grep -q "$letter_sequence" # "-q" 选项是用来禁止输出的. then echo "$letter_sequence found in $word" else echo "$letter_sequence not found in $word" fi if COMMAND_WHOSE_EXIT_STATUS_IS_0_UNLESS_ERROR_OCCURRED then echo "Command succeeded." else echo "Command failed." fi ``` 一个if/then结构可以包含嵌套的比较操作和条件判断操作. ``` if echo "Next *if* is part of the comparison for the first *if*." if [[ $comparison = "integer" ]] then (( a < b )) else [[ $a < $b ]] fi then echo '$a is less than $b' fi ``` ``` #!/bin/bash # 小技巧: # 如果你不能够确定一个特定的条件该如何进行判断, #+ 那么就使用if-test结构. echo echo "Testing \"0\"" if [ 0 ] # zero then echo "0 is true." else echo "0 is false." fi # 0 为真. echo echo "Testing \"1\"" if [ 1 ] # one then echo "1 is true." else echo "1 is false." fi # 1 为真. echo echo "Testing \"-1\"" if [ -1 ] # 负1 then echo "-1 is true." else echo "-1 is false." fi # -1 为真. echo echo "Testing \"NULL\"" if [ ] # NULL (空状态) then echo "NULL is true." else echo "NULL is false." fi # NULL 为假. echo echo "Testing \"xyz\"" if [ xyz ] # 字符串 then echo "Random string is true." else echo "Random string is false." fi # 随便的一串字符为真. echo echo "Testing \"\$xyz\"" if [ $xyz ] # 判断$xyz是否为null, 但是... # 这只是一个未初始化的变量. then echo "Uninitialized variable is true." else echo "Uninitialized variable is false." fi # 未定义的初始化为假. echo echo "Testing \"-n \$xyz\"" if [ -n "$xyz" ] # 更加正规的条件检查. then echo "Uninitialized variable is true." else echo "Uninitialized variable is false." fi # 未初始化的变量为假. echo xyz= # 初始化了, 但是赋null值. echo "Testing \"-n \$xyz\"" if [ -n "$xyz" ] then echo "Null variable is true." else echo "Null variable is false." fi # null变量为假. echo # 什么时候"false"为真? echo "Testing \"false\"" if [ "false" ] # 看起来"false"只不过是一个字符串而已. then echo "\"false\" is true." #+ 并且条件判断的结果为真. else echo "\"false\" is false." fi # "false" 为真. echo echo "Testing \"\$false\"" # 再来一个, 未初始化的变量. if [ "$false" ] then echo "\"\$false\" is true." else echo "\"\$false\" is false." fi # "$false" 为假. # 现在, 我们得到了预期的结果. # 如果我们测试以下为初始化的变量"$true"会发生什么呢? echo exit 0 ``` 如果 if 和 then 在条件判断的同一行上的话, 必须使用分号来结束 if 表达式. #### Else if和elif > elif 是else if的缩写形式. > > if [ condition1 ] > then > command1 > command2 > command3 > elif [ condition2 ] > > \#与else if一样 > > then > command4 > command5 > else > default-command > fi if test condition-true 结构与 if [ condition-true ] 完全相同. ``` bash$ type test test is a shell builtin bash$ type '[' [ is a shell builtin bash$ type '[[' [[ is a shell keyword bash$ type ']]' ]] is a shell keyword bash$ type ']' bash: type: ]: not found ``` [[ ]]结构比[ ]结构更加通用. 这是一个 扩展的test命令 , 是从 ksh88 中引进的.在[[和]]之间所有的字符都不会发生文件名扩展或者单词分割, 但是会发生参数扩展和命令替换 ``` file=/etc/passwd if [[ -e $file ]] then echo "Password file exists." fi ``` > 使用[[ ... ]]条件判断结构, 而不是[ ... ], 能够防止脚本中的许多逻辑错误. 比如, > &&, ||, <, 和> 操作符能够正常存在于[[ ]]条件判断结构中, 但是如果出现在[ ]结构中 > 的话, 会报错 (( ))结构扩展并计算一个算术表达式的值. ``` #!/bin/bash # 算术测试. # (( ... ))结构可以用来计算并测试算术表达式的结果. # 退出状态将会与[ ... ]结构完全相反! (( 0 )) echo "Exit status of \"(( 0 ))\" is $?." # 1 (( 1 )) echo "Exit status of \"(( 1 ))\" is $?." # 0 (( 5 > 4 )) # 真 echo "Exit status of \"(( 5 > 4 ))\" is $?." # 0 (( 5 > 9 )) # 假 echo "Exit status of \"(( 5 > 9 ))\" is $?." # 1 (( 5 - 5 )) # 0 echo "Exit status of \"(( 5 - 5 ))\" is $?." # 1 (( 5 / 4 )) # 除法也可以. echo "Exit status of \"(( 5 / 4 ))\" is $?." # 0 (( 1 / 2 )) # 除法的计算结果 < 1. echo "Exit status of \"(( 1 / 2 ))\" is $?." # 截取之后的结果为 0. # 1 (( 1 / 0 )) 2>/dev/null # 除数为0, 非法计算. # ^^^^^^^^^^^ echo "Exit status of \"(( 1 / 0 ))\" is $?." # 1 # "2>/dev/null"起了什么作用? # 如果这句被删除会怎样? # 尝试删除这句, 然后在运行这个脚本. exit 0 ``` ### 文件测试操作符 > 如果下面的条件成立将会返回真. > -e > 文件存在 > -a > 文件存在 > 这个选项的效果与-e相同. 但是它已经被"弃用"了, 并且不鼓励使用. > -f > 表示这个文件是一个 一般 文件(并不是目录或者设备文件) > -s > 文件大小不为零 > -d > 表示这是一个目录 > -b > 表示这是一个块设备(软盘, 光驱, 等等.) > -c > 表示这是一个字符设备(键盘, modem, 声卡, 等等.) > -p > 这个文件是一个管道 > -h > 这是一个符号链接 > -L > 这是一个符号链接 > -S > 表示这是一个socket > -t > 文件(描述符)被关联到一个终端设备上 > 这个测试选项一般被用来检测脚本中的 stdin ( [ -t 0 ] ) 或者 stdout ( [ -t 1 ] )是否来自于一个 > 终端. > -r > 文件是否具有可读权限( 指的是正在运行这个测试命令的用户是否具有读权限 ) > -w > 文件是否具有可写权限(指的是正在运行这个测试命令的用户是否具有写权限) > -x > 文件是否具有可执行权限(指的是正在运行这个测试命令的用户是否具有可执行权限) > -g > set-group-id(sgid)标记被设置到文件或目录上 > 如果目录具有 sgid 标记的话, 那么在这个目录下所创建的文件将属于拥有这个目录的用户组, 而 > 不必是创建这个文件的用户组. 这个特性对于在一个工作组中共享目录非常有用. > -u > set-user-id (suid)标记被设置到文件上 > 如果一个 root 用户所拥有的二进制可执行文件设置了 set-user-id 标记位的话, 那么普通用户也会 > 以 root 权限来运行这个文件. [1] 这对于需要访问系统硬件的执行程序(比如pppd和cdrecord)非 > 常有用. 如果没有 suid 标志的话, 这些二进制执行程序是不能够被非root用户调用的. > > ``` > -rwsr-xr-t 1 root 178236 Oct 2 2000 > /usr/sbin/pppd > ``` > > 对于设置了 suid 标志的文件, 在它的权限列中将会以 s 表示. > -k > 设置 粘贴位 > 对于"粘贴位"的一般了解, save-text-mode 标志是一个文件权限的特殊类型. 如果文件设置了这 > 个标志, 那么这个文件将会被保存到缓存中, 这样可以提高访问速度. [2] 粘贴位如果设置在目 > 录中, 那么它将限制写权限. 对于设置了粘贴位的文件或目录, 在它们的权限标记列中将会显 > 示 t . > > ``` > drwxrwxrwt 7 root 1024 May 19 21:26 tmp/ > ``` > > 如果用户并不拥有这个设置了粘贴位的目录, 但是他在这个目录下具有写权限, 那么这个用户只 > 能在这个目录下删除自己所拥有的文件. 这将有效的防止用户在一个公共目录中不慎覆盖或者删 > 除别人的文件. 比如说 /tmp 目录. (当然, 目录的所有者或者 root 用户可以随意删除或重命名其中 > 的文件.) > -O > 判断你是否是文件的拥有者 > -G > 文件的group-id是否与你的相同 > -N > 从文件上一次被读取到现在为止, 文件是否被修改过 > f1 -nt f2 > 文件 f1 比文件 f2 新 > f1 -ot f2 > 文件 f1 比文件 f2 旧 > f1 -ef f2 > 文件 f1 和文件 f2 是相同文件的硬链接 > ! > "非" -- 反转上边所有测试的结果(如果没给出条件, 那么返回真). ``` #!/bin/bash # broken-link.sh #一个纯粹的shell脚本用来找出那些断掉的符号链接文件并且输出它们所指向的文件. #以便于它们可以把输出提供给xargs来进行处理 :) #比如. broken-link.sh /somedir /someotherdir|xargs rm # #下边的方法, 不管怎么说, 都是一种更好的办法: # #find "somedir" -type l -print0|\ #xargs -r0 file|\ #grep "broken symbolic"| #sed -e 's/^\|: *broken symbolic.*$/"/g' # #但这不是一个纯粹的bash脚本, 最起码现在不是. #注意: 谨防在/proc文件系统和任何死循环链接中使用! ############################################################## #如果没有参数被传递到脚本中, 那么就使用 #当前目录. 否则就是用传递进来的参数作为目录 #来搜索. #################### [ $# -eq 0 ] && directorys=`pwd` || directorys=$@ #编写函数linkchk用来检查传递进来的目录或文件是否是链接, #并判断这些文件或目录是否存在. 然后打印它们所指向的文件. #如果传递进来的元素包含子目录, #那么把子目录也放到linkcheck函数中处理, 这样就达到了递归的目的. ########## linkchk () { for element in $1/*; do [ -h "$element" -a ! -e "$element" ] && echo \"$element\" [ -d "$element" ] && linkchk $element # 当然, '-h'用来测试符号链接, '-d'用来测试目录. done } #把每个传递到脚本的参数都送到linkchk函数中进行处理, #检查是否有可用目录. 如果没有, 那么就打印错误消息和 #使用信息. ################ for directory in $directorys; do if [ -d $directory ] then linkchk $directory else echo "$directory is not a directory" echo "Usage: $0 dir1 dir2 ..." fi done exit 0 ``` ### 其他比较操作符 P86 整数比较 | operator | meanings | example | | -------- | ----------------------------- | ---------------------- | | -eq | 等于 | if [ "\$a" -eq "\$b" ] | | -ne | 不等于 | if [ "\$a" -ne "\$b" ] | | -gt | 大于 | if [ "\$a" -gt "\$b" ] | | -ge | 大于等于 | if [ "\$a" -ge "\$b" ] | | -lt | 小于 | if [ "\$a" -lt "\$b" ] | | -le | 小于等于 | if [ "\$a" -le "\$b" ] | | < <= | 小于 小于等于(在双括号中使用) | (("\$a" < "\$b")) | | > >= | 大于 大于等于(在双括号中使用) | (("\$a" > "\$b")) | 字符串比较 | = | meanings | example | | ---- | -------------------------------------- | ---------------------------------------------- | | = | 等于 | if [ "\$a" = "\$b" ] | | == | 等于 | if [ "\$a" == "\$b" ] | | != | 不等号 | if [ "\$a" != "\$b" ] | | < | 小于, 按照ASCII字符进行排序 | if [ "\$a" \\< "\$b" ] if [ ["\$a" < "\$b"] ] | | > | 大于, 按照ASCII字符进行排序 | if [ "\$a" \\> "\$b" ] if [ ["\$a" > "\$b"] ] | | -z | 字符串为"null", 意思就是字符串长度为零 | | | -n | 字符串不为"null". | | ``` [[ $a == z* ]] # 如果$a以"z"开头(模式匹配)那么结果将为真 [[ $a == "z*" ]] # 如果$a与z*相等(就是字面意思完全一样), 那么结果为真. [ $a == z* ] # 文件扩展匹配(file globbing)和单词分割有效. [ "$a" == "z*" ] # 如果$a与z*相等(就是字面意思完全一样), 那么结果为真. ``` ``` #!/bin/bash a=4 b=5 # 这里的"a"和"b"既可以被认为是整型也可被认为是字符串. # 这里在算术比较与字符串比较之间是容易让人产生混淆, #+ 因为Bash变量并不是强类型的. # Bash允许对于变量进行整形操作与比较操作. #+ 但前提是变量中只能包含数字字符. # 不管怎么样, 还是要小心. echo if [ "$a" -ne "$b" ] then echo "$a is not equal to $b" echo "(arithmetic comparison)" fi echo if [ "$a" != "$b" ] then echo "$a is not equal to $b." echo "(string comparison)" # "4" != "5" # ASCII 52 != ASCII 53 fi # 在这个特定的例子中, "-ne"和"!="都可以. echo exit 0 ``` -a 逻辑与 -o 逻辑或 这与Bash中的比较操作符&&和||非常相像, 但是这个两个操作符是用在双中括号结构中的. ### 嵌套的if/then条件测试 > if [ condition1 ] > then > if [ condition2 ] > then > do-something # But only if both "condition1" and "condition2" valid. > fi > fi ## 操作符与相关主题 P93 ### 操作符 | operator | meanings | | ------------------ | ------------------------------- | | = | 赋值 | | + - * / | 加减乘除 | | ** | 幂运算 | | % | 模运算 | | += -= *= /= %= | x 等于 | | << | 左移位(每次左移都相当于乘以 2 ) | | <<= | 左移赋值 | | >>= | 右移赋值 | | & &= | 按位与 按位与赋值 | | \| \|= | 按位或 按位或赋值 | | ~ | 按位反 | | ! | 按位非 | | ^ ^= | 按位异或XOR 按位异或赋值 | | && | 与 | | \|\| | 或 | | , | 逗号操作符 连接两个或多个算术运 | ``` #!/bin/bash # 使用10种不同的方法计数到11. n=1; echo -n "$n " let "n = $n + 1" # let "n = n + 1" 也可以. echo -n "$n " : $((n = $n + 1)) # ":" 是必需的, 因为如果没有":"的话, #+ Bash将会尝试把"$((n = $n + 1))"解释为一个命令. echo -n "$n " (( n = n + 1 )) # 上边这句是一种更简单方法. echo -n "$n " n=$(($n + 1)) echo -n "$n " : $[ n = $n + 1 ] # ":" 是必需的, 因为如果没有":"的话, #+ Bash将会尝试把"$[ n = $n + 1 ]"解释为一个命令. # 即使"n"被初始化为字符串, 这句也能够正常运行. echo -n "$n " n=$[ $n + 1 ] # 即使"n"被初始化为字符串, 这句也能够正常运行. #* 应该尽量避免使用这种类型的结构, 因为它已经被废弃了, 而且不具可移植性. echo -n "$n " # 现在来一个C风格的增量操作. let "n++" # let "++n" 也可以. echo -n "$n " (( n++ )) # (( ++n ) 也可以. echo -n "$n " : $(( n++ )) # : $(( ++n )) 也可以. echo -n "$n " : $[ n++ ] # : $[ ++n ]] 也可以. echo -n "$n " echo exit 0 ``` Bash不能够处理浮点运算 ``` #!/bin/bash a=24 b=47 if [ "$a" -eq 24 ] && [ "$b" -eq 47 ] then echo "Test #1 succeeds." else echo "Test #1 fails." fi # ERROR: if [ "$a" -eq 24 && "$b" -eq 47 ] #+ 尝试运行' [ "$a" -eq 24 ' #+ 因为没找到匹配的']'所以失败了. # # 注意: if [[ $a -eq 24 && $b -eq 24 ]] 也能正常运行. # 双中括号的if-test结构要比 #+ 单中括号的if-test结构更加灵活. # (在第17行"&&"与第6行的"&&"具有不同的含义.) if [ "$a" -eq 98 ] || [ "$b" -eq 47 ] then echo "Test #2 succeeds." else echo "Test #2 fails." fi # -a和-o选项提供了 #+ 一种可选的混合条件测试的方法. if [ "$a" -eq 24 -a "$b" -eq 47 ] then echo "Test #3 succeeds." else echo "Test #3 fails." fi if [ "$a" -eq 98 -o "$b" -eq 47 ] then echo "Test #4 succeeds." else echo "Test #4 fails." fi a=rhino b=crocodile if [ "$a" = rhino ] && [ "$b" = crocodile ] then echo "Test #5 succeeds." else echo "Test #5 fails." fi exit 0 ``` 逗号 ``` let "t1 = ((5 + 3, 7 - 1, 15 - 4))" echo "t1 = $t1" # t1 = 11 let "t2 = ((a = 9, 15 / 3))" # 设置"a"并且计算"t2". echo "t2 = $t2 a = $a" # t2 = 5 a = 9 ``` ### 数字常量 > shell脚本在默认情况下都是把数字作为10进制数来处理, 除非这个数字采用了特殊的标记或者前缀. > 如果数字以 0 开头的话那么就是 8进制 数. 如果数字以 0x 开头的话那么就是 16进制 数. 如果数字中间嵌入 > 了 # 的话, 那么就被认为是 BASE#NUMBER 形式的标记法(有范围和符号限制). ``` #!/bin/bash # numbers.sh: 几种不同数制的数字表示法. # 10进制: 默认情况 let "dec = 32" echo "decimal number = $dec" # 32 # 这没什么特别的. # 8进制: 以'0'(零)开头 let "oct = 032" echo "octal number = $oct" # 26 # 表达式结果是用10进制表示的. # --------------------------- # 16进制: 以'0x'或者'0X'开头的数字 let "hex = 0x32" echo "hexadecimal number = $hex" # 50 # 表达式结果是用10进制表示的. # 其他进制: BASE#NUMBER # BASE的范围在2到64之间. # NUMBER的值必须使用BASE范围内的符号来表示, 具体看下边的示例. let "bin = 2#111100111001101" echo "binary number = $bin" # 31181 let "b32 = 32#77" echo "base-32 number = $b32" # 231 let "b64 = 64#@_" echo "base-64 number = $b64" # 4031 # 这个表示法只能工作于受限的ASCII字符范围(2 - 64). # 10个数字 + 26个小写字母 + 26个大写字符 + @ + _ echo echo $((36#zz)) $((2#10101010)) $((16#AF16)) $((53#1aA)) # 1295 170 44822 3375 # 重要的注意事项: # --------------- # 使用一个超出给定进制的数字的话, #+ 将会引起一个错误. let "bad_oct = 081" # (部分的) 错误消息输出: # bad_oct = 081: value too great for base (error token is "081") # Octal numbers use only digits in the range 0 - 7. exit 0 ```
作者:じ☆ve不哭
Bash 进阶 P101 ## 变量重游 ### 内部变量 | 变量名($名称) | 意思 | | ------------------------------- | ------------------------------------------------------------ | | BASH | Bash 的二进制程序文件的路径 | | BASH_ENV | Bash的启动文件 | | BASH_SUBSHELL | 提示子shell的层次 | | BASH_VERSINFO[n] | 6个元素的数组, 它包含了所安装的Bash的版本信息 | | BASH_VERSION | Bash版本号 | | DIRSTACK | 在目录栈中最顶端的值 | | EDITOR | 脚本所调用的默认编辑器, 通常情况下是vi或者是emacs. | | EUID | "有效"用户ID | | FUNCNAME | 当前函数的名字 | | GLOBIGNORE | 一个文件名的模式匹配列表 | | GROUPS | 目前用户所属的组 | | HOME | 用户的home目录, 一般是 /home/username | | HOSTNAME | 在一个初始化脚本中, 在系统启动的时候分配一个系统名字 | | $HOSTTYPE | 主机类型 | | IFS | 内部域分隔符 决定Bash在解释字符串时如何识别域, 或者单词边界. | | IGNOREEOF | 忽略EOF: 告诉shell在log out之前要忽略多少文件结束符(control-D) | | LC_COLLATE | 常在 .bashrc 或 /etc/profile 中设置, 这个变量用来控制文件名扩展和模式匹配的展开顺序 | | LC_CTYPE | 来控制通配(globbing)和模式匹配中的字符串解释 | | LINENO | 自身在脚本中所在的行号 | | MACHTYPE | 机器类型 | | OLDPWD | 之前的工作目录("OLD-print-working-directory", 就是之前你所在的目录) | | OSTYPE | 操作系统类型 | | PATH | 可执行文件的搜索路径, 一般为 /usr/bin/ , /usr/X11R6/bin/ , /usr/local/bin , 等等. | | PIPESTATUS | 最后一个运行的 前台 管道的退出状态码. | | PPID | 进程的 $PPID 就是这个进程的父进程的进程ID( pid ) | | PROMPT_COMMAND | 主提示符 $PS1 显示之前需要执行的命令 | | PS1 PS2 PS3 PS4 | 第几条提示符 | | PWD | 工作目录(你当前所在的目录) 与内建命令pwd作用相同 | | REPLY | 当没有参数变量提供给read命令的时候, 这个变量会作为默认变量提供给read命令. | | SECONDS | 脚本已经运行的时间 | | SHELLOPTS | hell中已经激活的选项的列表, 这是一个只读变量 | | SHLVL | Shell级别, 就是Bash被嵌套的深度. 如果是在命令行中, 那么\$SHLVL为1, 如果在脚本中那么\$SHLVL为2 | | TMOUT | 如果 $TMOUT 环境变量被设置为非零值 time 的话, 那么经过 time 秒后, shell提示符将会超时. 这将<br/>会导致登出(logout). | | UID | 用户ID号 当前用户的用户标识号, 记录在 /etc/passwd 文件中 | | \$0 , \$1 , \$2 | 位置参数, 从命令行传递到脚本, 或者传递给函数 或者set给变量 | | $#<br>\$* <br>\$@ | 命令行参数 或者位置参数的个数 <br>所有的位置参数都被看作为一个单词 <br> 与$*相同, 但是每个参数都是一个独立的引用字符串 | | $-<br>\$!<br>\$_<br>\$?<br>\$\$ | 传递给脚本的标记<br>运行在后台的最后一个作业的PID(进程ID)<br>之前执行的命令的最后一个参数的值.<br>命令, 函数, 或者是脚本本身的退出状态码<br>脚本自身的进程ID. | ``` #!/bin/bash # $IFS 处理空白与处理其他字符不同. output_args_one_per_line() { for arg do echo "[$arg]" done } echo; echo "IFS=\" \"" echo "-------" IFS=" " var=" a b c " output_args_one_per_line $var # output_args_one_per_line `echo " a b c "` # # [a] # [b] # [c] echo; echo "IFS=:" echo "-----" IFS=: var=":a::b:c:::" # 与上边一样, 但是用" "替换 了":". output_args_one_per_line $var # # [] # [a] # [] # [b] # [c] # [] # [] # [] # 同样的事情也会发生在awk的"FS"域中. echo exit 0 ``` ``` #!/bin/bash # reply.sh # REPLY是提供给'read'命令的默认变量. echo echo -n "What is your favorite vegetable? " read echo "Your favorite vegetable is $REPLY." # 当且仅当没有变量提供给"read"命令时, ``` ``` #!/bin/bash # timed-input.sh # TMOUT=3 在新一些的Bash版本上也能运行的很好. TIMELIMIT=3 # 这个例子中设置的是3秒. 也可以设置为其他的时间值. PrintAnswer() { if [ "$answer" = TIMEOUT ] then echo $answer else # 别和上边的例子弄混了. echo "Your favorite veggie is $answer" kill $! # 不再需要后台运行的TimerOn函数了, kill了吧. # $! 变量是上一个在后台运行的作业的PID. fi } TimerOn() { sleep $TIMELIMIT && kill -s 14 $$ & # 等待3秒, 然后给脚本发送一个信号. } Int14Vector() { answer="TIMEOUT" PrintAnswer exit 14 } trap Int14Vector 14 # 定时中断(14)会暗中给定时间限制. echo "What is your favorite vegetable " TimerOn read answer PrintAnswer # 无可否认, 这是一个定时输入的复杂实现, #+ 然而"read"命令的"-t"选项可以简化这个任务. # 参考后边的"t-out.sh". # 如果你需要一个真正优雅的写法... #+ 建议你使用C或C++来重写这个应用, #+ 你可以使用合适的函数库, 比如'alarm'和'setitimer'来完成这个任务. exit 0 ``` ``` #!/bin/bash # t-out.sh # 从"syngin seven"的建议中得到的灵感 (感谢). TIMELIMIT=4 # 4秒 read -t $TIMELIMIT variable <&1 # ^^^ # 在这个例子中, 对于Bash 1.x和2.x就需要"<&1"了, # 但是Bash 3.x就不需要. echo if [ -z "$variable" ] # 值为null? then echo "Timed out, variable still unset." else echo "variable = $variable" fi exit 0 ``` ``` #!/bin/bash # arglist.sh # 多使用几个参数来调用这个脚本, 比如"one two three". E_BADARGS=65 if [ ! -n "$1" ] then echo "Usage: `basename $0` argument1 argument2 etc." exit $E_BADARGS fi echo index=1 # 起始计数. echo "Listing args with \"\$*\":" for arg in "$*" # 如果"$*"不被""引用,那么将不能正常地工作. do echo "Arg #$index = $arg" let "index+=1" done # $* 将所有的参数看成一个单词. echo "Entire arg list seen as single word." echo index=1 # 重置计数(译者注: 从1开始). # 如果你写这句会发生什么? echo "Listing args with \"\$@\":" for arg in "$@" do echo "Arg #$index = $arg" let "index+=1" done # $@ 把每个参数都看成是单独的单词. echo "Arg list seen as separate words." echo index=1 # 重置计数(译者注: 从1开始). echo "Listing args with \$* (unquoted):" for arg in $* do echo "Arg #$index = $arg" let "index+=1" done # 未引用的$*将会把参数看成单独的单词. echo "Arg list seen as separate words." exit 0 ``` ``` #!/bin/bash # 使用 ./scriptname 1 2 3 4 5 来调用这个脚本 echo "$@" # 1 2 3 4 5 shift echo "$@" # 2 3 4 5 shift echo "$@" # 3 4 5 # 每次"shift"都会丢弃$1. # "$@" 将包含剩下的参数. ``` ### 操作字符串 expr 介绍 > 将表达式的值列印到标准输出,分隔符下面的空行可提升算式优先级。 > 可用的表达式有: > > ARG1 | ARG2 若ARG1 的值不为0 或者为空,则返回ARG1,否则返回ARG2 > > ARG1 & ARG2 若两边的值都不为0 或为空,则返回ARG1,否则返回 0 > > ARG1 < ARG2 ARG1 小于ARG2 > ARG1 <= ARG2 ARG1 小于或等于ARG2 > ARG1 = ARG2 ARG1 等于ARG2 > ARG1 != ARG2 ARG1 不等于ARG2 > ARG1 >= ARG2 ARG1 大于或等于ARG2 > ARG1 > ARG2 ARG1 大于ARG2 > > ARG1 + ARG2 计算 ARG1 与ARG2 相加之和 > ARG1 - ARG2 计算 ARG1 与ARG2 相减之差 > > ARG1 * ARG2 计算 ARG1 与ARG2 相乘之积 > ARG1 / ARG2 计算 ARG1 与ARG2 相除之商 > ARG1 % ARG2 计算 ARG1 与ARG2 相除之余数 > > 字符串 : 表达式 定位字符串中匹配表达式的模式 > > match 字符串 表达式 等于"字符串 :表达式" > substr 字符串 偏移量 长度 替换字符串的子串,偏移的数值从 1 起计 > index 字符串 字符 在字符串中发现字符的地方建立下标,或者标0 > length 字符串 字符串的长度 > + 记号 将记号解析为字符串,即使它是一个类似"match"或 > 运算符"/"那样的关键字 > > ( 表达式 ) 表达式的值 > > 请注意有许多运算操作符都可能需要由 shell 先实施转义。 > 如果参与运算的 ARG 自变量都是数字,比较符就会被视作数学符号,否则就是多义的。 > 模式匹配会返回"\"和"\"之间被匹配的子字符串或空(null);如果未使用"\"和"\", > 则会返回匹配字符数量或是 0。 > > 若表达式的值既不是空也不是 0,退出状态值为 0;若表达式的值为空或为 0, > 退出状态值为 1。如果表达式的句法无效,则会在出错时返回退出状态值 3。 **字符串长度** > - ${#string} > - expr length \$string > - expr "$string" : '.* ``` stringZ=abcABC123ABCabc echo ${#stringZ} # 15 echo `expr length $stringZ` # 15 echo `expr "$stringZ" : '.*'` # 15 ``` **匹配字符串开头的子串长度** > - expr match "\$string" '\$substring' > - expr "\$string" :'\$substring' '\$substring' 为正则表达式 ``` stringZ=abcABC123ABCabc # |------| echo `expr match "$stringZ" 'abc[A-Z]*.2'` # 8 echo `expr "$stringZ" : 'abc[A-Z]*.2'` # 8 ``` **索引** > expr index \$string $substring ``` stringZ=abcABC123ABCabc echo `expr index "$stringZ" C12` # 6 # C 字符的位置. echo `expr index "$stringZ" 1c` # 3 # 'c' (in #3 position) matches before '1'. ``` **提取子串** > - \${string:position} 从位置\$position 开始提取子串 > - ${string:position:length} 从位置 \$position 开始提取 \$length 长度的子串. > - expr substr \$string \$position \$length 在 \$string 中从 \$position 开始提取 \$length 长度的子串. > - expr match "$string" '\\(\$substring\\)' > - expr "$string" : '\\(\$substring\\)' ``` stringZ=abcABC123ABCabc # 0123456789..... # 0-based indexing. echo ${stringZ:0} # abcABC123ABCabc echo ${stringZ:1} # bcABC123ABCabc echo ${stringZ:7} # 23ABCabc echo ${stringZ:7:3} # 23A # 提取子串长度为3. # 能不能从字符串的右边(也就是结尾)部分开始提取子串? echo ${stringZ:-4} # abcABC123ABCabc # 默认是提取整个字符串, 就象${parameter:-default}一样. # 然而 . . . echo ${stringZ:(-4)} # Cabc echo ${stringZ: -4} # Cabc # 这样, 它就可以工作了. # 使用圆括号或者添加一个空格可以"转义"这个位置参数. ``` ``` stringZ=abcABC123ABCabc 2 # 123456789...... 3 # 以1开始计算. 4 5 echo `expr substr $stringZ 1 2` # ab 6 echo `expr substr $stringZ 4 3` # ABC ``` **子串削除** > - \${string#substring} 从 \$string 的 **开头** 位置截掉 **最短** 匹配的 \$substring . > - \${string##substring} 从 \$string 的 **开头** 位置截掉 **最长** 匹配的 \$substring . > - ${string%substring} 从 \$string 的 **结尾** 位置截掉 **最短** 匹配的 \$substring . > - ${string%%substring} 从 \$string 的 **结尾** 位置截掉 **最长** 匹配的 \$substring . ``` stringZ=abcABC123ABCabc # |----| # |----------| echo ${stringZ#a*C} # 123ABCabc # 截掉'a'到'C'之间最短的匹配字符串. echo ${stringZ##a*C} # abc # 截掉'a'到'C'之间最长的匹配字符串. stringZ=abcABC123ABCabc # || # |------------| echo ${stringZ%b*c} # abcABC123ABCa # 从$stringZ的结尾位置截掉'b'到'c'之间最短的匹配. echo ${stringZ%%b*c} # a # 从$stringZ的结尾位置截掉'b'到'c'之间最长的匹配. ``` ``` #!/bin/bash # getopt-simple.sh getopt_simple() { echo "getopt_simple()" echo "Parameters are '$*'" until [ -z "$1" ] do echo "Processing parameter of: '$1'" if [ ${1:0:1} = '/' ] then tmp=${1:1} # 去掉开头的'/' . . . parameter=${tmp%%=*} # 提取参数名. value=${tmp##*=} # 提取参数值. echo "Parameter: '$parameter', value: '$value'" eval $parameter=$value fi shift done } # 把所有选项传给函数getopt_simple(). getopt_simple $* echo "test is '$test'" echo "test2 is '$test2'" exit 0 --- sh getopt_example.sh /test=value1 /test2=value2 Parameters are '/test=value1 /test2=value2' Processing parameter of: '/test=value1' Parameter: 'test', value: 'value1' Processing parameter of: '/test2=value2' Parameter: 'test2', value: 'value2' test is 'value1' test2 is 'value2' ``` **子串替换** > - ${string/substring/replacement} 替换第一个匹配 > - ${string//substring/replacement} 替换第所有匹配 > - ${string/#substring/replacement} 替换开头匹配 > - ${string/%substring/replacement} 替换结尾匹配 ``` stringZ=abcABC123ABCabc echo ${stringZ/abc/xyz} # xyzABC123ABCabc echo ${stringZ//abc/xyz} # xyzABC123ABCxyz echo ${stringZ/#abc/XYZ} # XYZABC123ABCabc echo ${stringZ/%abc/XYZ} # abcABC123ABCXYZ ``` ### 使用awk来处理字符串 ``` #!/bin/bash # substring-extraction.sh String=23skidoo1 # 012345678 Bash # 123456789 awk # 注意不同的字符串索引系统: # Bash的第一个字符是从'0'开始记录的. # Awk的第一个字符是从'1'开始记录的. echo ${String:2:4} # 位置 3 (0-1-2), 4 个字符长 # skid # awk中等价于${string:pos:length}的命令是substr(string,pos,length). echo | awk ' { print substr("'"${String}"'",3,4) # skid } ' # 使用一个空的"echo"通过管道传递给awk一个假的输入, #+ 这样就不必提供一个文件名给awk. exit 0 ``` ### 参数替换 **处理和(或)扩展变量** > - \${parameter} 与 $parameter 相同, 也就是变量 parameter 的值 > - \${parameter-default} 如果变量parameter没被声明, 那么就使用默认值 > - \${parameter:-default} 如果变量parameter没被设置, 那么就使用默认值. > - \${parameter=default} , \${parameter:=default}} 如果变量parameter没被设置, 那么就使用默认值. > - ${parameter+alt_value} , \${parameter:+alt_value}} parameter没声明或者设置, 么就使用null ,否则使用alt_value. > - ${parameter?err_msg} 未声明 打印 err_msg > - ${parameter:?err_msg}未设置 打印 err_msg ``` #!/bin/bash # param-sub.sh # 一个变量是否被声明或设置, #+ 将会影响这个变量是否使用默认值, #+ 即使这个变量值为空(null). username0= echo "username0 has been declared, but is set to null." echo "username0 = ${username0-`whoami`}" # 不会有输出. echo echo username1 has not been declared. echo "username1 = ${username1-`whoami`}" # 将会输出默
作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="outline: 0px;font-family: system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);visibility: visible;"> <p style="outline: 0px;visibility: visible;"><strong style="outline: 0px;visibility: visible;"><span style="outline: 0px;font-size: 20px;visibility: visible;">简单工厂(非23种设计模式中的一种)</span></strong><br style="outline: 0px;visibility: visible;"></p> <section data-mpa-template="t" mpa-from-tpl="t" style="outline: 0px;visibility: visible;"> <p style="outline: 0px;visibility: visible;"><br style="outline: 0px;visibility: visible;"></p> <section data-mpa-template="t" mpa-from-tpl="t" style="outline: 0px;visibility: visible;"> <h4 data-tool="mdnice编辑器" style="margin-bottom: 15px;outline: 0px;color: rgb(34, 34, 34);letter-spacing: 0.544px;caret-color: rgb(51, 51, 51);line-height: 2em;visibility: visible;"><span style="outline: 0px;font-size: 17px;visibility: visible;"><strong style="outline: 0px;visibility: visible;"><span mpa-is-content="t" style="outline: 0px;color: rgb(0, 122, 170);visibility: visible;">实现方式:</span></strong></span></h4> </section> </section> <p style="outline: 0px;visibility: visible;"><span style="outline: 0px;font-size: 16px;letter-spacing: 0px;visibility: visible;">BeanFactory。Spring中的BeanFactory就是简单工厂模式的体现,根据传入一个唯一的标识来获得Bean对象,但是否是在传入参数后创建还是传入参数前创建这个要根据具体情况来定。</span></p> <p style="outline: 0px;visibility: visible;"><span style="outline: 0px;letter-spacing: 0px;visibility: visible;"><br style="outline: 0px;visibility: visible;"></span></p> <section data-mpa-template="t" mpa-from-tpl="t" style="outline: 0px;visibility: visible;"> <section data-mpa-template="t" mpa-from-tpl="t" style="outline: 0px;visibility: visible;"> <h4 data-tool="mdnice编辑器" style="margin-bottom: 15px;outline: 0px;color: rgb(34, 34, 34);letter-spacing: 0.544px;caret-color: rgb(51, 51, 51);line-height: 2em;visibility: visible;"><strong style="outline: 0px;visibility: visible;"><span style="outline: 0px;font-size: 17px;color: rgb(0, 122, 170);visibility: visible;">实质:</span></strong></h4> </section> </section> <p style="outline: 0px;visibility: visible;"><span style="outline: 0px;letter-spacing: 0px;font-size: 16px;visibility: visible;">由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类。</span><br style="outline: 0px;visibility: visible;"></p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;outline: 0px;font-weight: bold;font-size: 22px;visibility: visible;"><strong mpa-from-tpl="t" style="outline: 0px;font-size: 17px;color: rgb(34, 34, 34);letter-spacing: 0.544px;caret-color: rgb(51, 51, 51);visibility: visible;"><span mpa-is-content="t" style="outline: 0px;color: rgb(0, 122, 170);visibility: visible;">实现原理:</span></strong><br style="outline: 0px;visibility: visible;"></h2> <p style="outline: 0px;"><span style="outline: 0px;font-size: 16px;"><strong style="outline: 0px;letter-spacing: 0px;">bean容器的启动阶段:</strong><br style="outline: 0px;"></span></p> <ul data-tool="mdnice编辑器" class="list-paddingleft-1" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;outline: 0px;width: 557.438px;"> <li style="outline: 0px;font-size: 16px;"> <section style="margin-top: 5px;margin-bottom: 5px;outline: 0px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 26px;color: black;"><span style="outline: 0px;">读取bean的xml配置文件,将bean元素分别转换成一个BeanDefinition对象。</span></p> </section></li> <li style="outline: 0px;font-size: 16px;"> <section style="margin-top: 5px;margin-bottom: 5px;outline: 0px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 26px;color: black;"><span style="outline: 0px;">然后通过BeanDefinitionRegistry将这些bean注册到beanFactory中,保存在它的一个ConcurrentHashMap中。</span></p> </section></li> <li style="outline: 0px;"> <section style="margin-top: 5px;margin-bottom: 5px;outline: 0px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 26px;color: black;"><span style="outline: 0px;font-size: 16px;">将BeanDefinition注册到了beanFactory之后,在这里Spring为我们提供了一个扩展的切口,允许我们通过实现接口BeanFactoryPostProcessor 在此处来插入我们定义的代码。</span></p> <p style="padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 26px;color: black;"><span style="outline: 0px;font-size: 16px;">典型的例子就是:PropertyPlaceholderConfigurer,我们一般在配置数据库的dataSource时使用到的占位符的值,就是它注入进去的。</span></p> </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 26px;"><span style="outline: 0px;font-size: 16px;"><strong style="outline: 0px;">容器中bean的实例化阶段:</strong></span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 26px;"><span style="outline: 0px;font-size: 16px;">实例化阶段主要是通过反射或者CGLIB对bean进行实例化,在这个阶段Spring又给我们暴露了很多的扩展点:</span></p> <ul data-tool="mdnice编辑器" class="list-paddingleft-1" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;outline: 0px;width: 557.438px;"> <li style="outline: 0px;font-size: 16px;"> <section style="margin-top: 5px;margin-bottom: 5px;outline: 0px;line-height: 26px;color: rgb(1, 1, 1);"> <span style="outline: 0px;"><strong style="outline: 0px;color: black;">各种的Aware接口</strong> ,比如 BeanFactoryAware,对于实现了这些Aware接口的bean,在实例化bean时Spring会帮我们注入对应的BeanFactory的实例。</span> </section></li> <li style="outline: 0px;font-size: 16px;"> <section style="margin-top: 5px;margin-bottom: 5px;outline: 0px;line-height: 26px;color: rgb(1, 1, 1);"> <span style="outline: 0px;"><strong style="outline: 0px;color: black;">BeanPostProcessor接口</strong> ,实现了BeanPostProcessor接口的bean,在实例化bean时Spring会帮我们调用接口中的方法。</span> </section></li> <li style="outline: 0px;font-size: 16px;"> <section style="margin-top: 5px;margin-bottom: 5px;outline: 0px;line-height: 26px;color: rgb(1, 1, 1);"> <span style="outline: 0px;"><strong style="outline: 0px;color: black;">InitializingBean接口</strong> ,实现了InitializingBean接口的bean,在实例化bean时Spring会帮我们调用接口中的方法。</span> </section></li> <li style="outline: 0px;font-size: 16px;"> <section style="margin-top: 5px;margin-bottom: 5px;outline: 0px;line-height: 26px;color: rgb(1, 1, 1);"> <span style="outline: 0px;"><strong style="outline: 0px;color: black;">DisposableBean接口</strong> ,实现了BeanPostProcessor接口的bean,在该bean死亡时Spring会帮我们调用接口中的方法。</span> </section></li> </ul> <section style="margin-top: 5px;margin-bottom: 5px;outline: 0px;line-height: 26px;color: rgb(1, 1, 1);"> <br style="outline: 0px;"> </section> <section data-mpa-template="t" mpa-from-tpl="t" style="outline: 0px;"> <section data-mpa-template="t" mpa-from-tpl="t" style="outline: 0px;"> <h4 data-tool="mdnice编辑器" style="margin-bottom: 15px;outline: 0px;color: rgb(34, 34, 34);letter-spacing: 0.544px;caret-color: rgb(51, 51, 51);line-height: 2em;"><span style="outline: 0px;font-size: 17px;"><strong mpa-from-tpl="t" style="outline: 0px;"><span mpa-is-content="t" style="outline: 0px;color: rgb(0, 122, 170);">设计意义:</span></strong></span></h4> </section> </section> <p style="outline: 0px;"><span style="outline: 0px;font-size: 16px;"><strong style="outline: 0px;letter-spacing: 0px;">松耦合。</strong><span style="outline: 0px;letter-spacing: 0px;"> 可以将原来硬编码的依赖,通过Spring这个beanFactory这个工厂来注入依赖,也就是说原来只有依赖方和被依赖方,现在我们引入了第三方——spring这个beanFactory,由它来解决bean之间的依赖问题,达到了松耦合的效果.</span><br style="outline: 0px;"></span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 26px;"><span style="outline: 0px;font-size: 16px;"><strong style="outline: 0px;">bean的额外处理。</strong> 通过Spring接口的暴露,在实例化bean的阶段我们可以进行一些额外的处理,这些额外的处理只需要让bean实现对应的接口即可,那么spring就会在bean的生命周期调用我们实现的接口来处理该bean。</span><code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;outline: 0px;font-size: 14px;border-radius: 4px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;"><span style="outline: 0px;font-size: 16px;">[非常重要]</span></code></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 26px;"><span style="outline: 0px;font-size: var(--articleFontsize);letter-spacing: 0.034em;"><br style="outline: 0px;"></span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 26px;"><strong style="outline: 0px;"><span style="outline: 0px;letter-spacing: 0.034em;font-size: 20px;">2.工厂方法</span></strong></p> <p style="outline: 0px;"><strong mpa-from-tpl="t" style="outline: 0px;color: rgb(34, 34, 34);letter-spacing: 0.544px;caret-color: rgb(51, 51, 51);"><span mpa-is-content="t" style="outline: 0px;color: rgb(0, 122, 170);"><br style="outline: 0px;"></span></strong></p> <p style="outline: 0px;"><strong mpa-from-tpl="t" style="outline: 0px;color: rgb(34, 34, 34);letter-spacing: 0.544px;caret-color: rgb(51, 51, 51);"><span mpa-is-content="t" style="outline: 0px;color: rgb(0, 122, 170);">实现方式:</span></strong></p> <p style="outline: 0px;"><span style="outline: 0px;letter-spacing: 0px;font-size: var(--articleFontsize);"><br style="outline: 0px;"></span></p> <p style="outline: 0px;"><span style="outline: 0px;font-size: 16px;letter-spacing: 0px;">FactoryBean接口。</span></p> <p style="outline: 0px;"><span style="outline: 0px;letter-spacing: 0px;"><br style="outline: 0px;"></span></p> <section data-mpa-template="t" mpa-from-tpl="t" style="outline: 0px;"> <section data-mpa-template="t" mpa-from-tpl="t" style="outline: 0px;"> <h4 data-tool="mdnice编辑器" style="margin-bottom: 15px;outline: 0px;color: rgb(34, 34, 34);letter-spacing: 0.544px;caret-color: rgb(51, 51, 51);line-height: 2em;"><span style="outline: 0px;font-size: 17px;"><strong mpa-from-tpl="t" style="outline: 0px;"><span mpa-is-content="t" style="outline: 0px;color: rgb(0, 122, 170);">实现原理:</span></strong></span></h4> </section> </section> <p style="outline: 0px;"><span style="outline: 0px;letter-spacing: 0px;font-size: 16px;">实现了FactoryBean接口的bean是一类叫做factory的bean。其特点是,spring会在使用getBean()调用获得该bean时,会自动调用该bean的getObject()方法,所以返回的不是factory这个bean,而是这个bean.getOjbect()方法的返回值。</span></p> <p style="outline: 0px;"><br style="outline: 0px;"></p> <section data-mpa-template="t" mpa-from-tpl="t" style="outline: 0px;"> <section data-mpa-template="t" mpa-from-tpl="t" style="outline: 0px;"> <h4 data-tool="mdnice编辑器" style="margin-bottom: 15px;outline: 0px;color: rgb(34, 34, 34);letter-spacing: 0.544px;caret-color: rgb(51, 51, 51);line-height: 2em;"><span style="outline: 0px;font-size: 17px;"><strong mpa-from-tpl="t" style="outline: 0px;"><span mpa-is-content="t" style="outline: 0px;color: rgb(0, 122, 170);">例子:</span></strong></span></h4> </section> </section> <p style="outline: 0px;"><span style="outline: 0px;font-size: 16px;letter-spacing: 0px;">典型的例子有spring与mybatis的结合。<br style="outline: 0px;"></span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 26px;"><span style="outline: 0px;font-size: 16px;"><strong style="outline: 0px;">代码示例:</strong></span></p> <p style="outline: 0px;"><img class="rich_pages wxw-img" data-ratio="0.13366336633663367" src="/upload/71e594011cc704db4e54c09823406f2e.jpg" data-type="jpeg" data-w="606" style="outline: 0px;width: 606px !important;visibility: visible !important;"></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 26px;"><span style="outline: 0px;font-size: 16px;"><strong style="outline: 0px;">说明:</strong></span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 26px;"><span style="outline: 0px;font-size: 16px;">我们看上面该bean,因为实现了FactoryBean接口,所以返回的不是 SqlSessionFactoryBean 的实例,而是它的 SqlSessionFactoryBean.getObject() 的返回值。</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 26px;"><br style="outline: 0px;"></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 26px;"><strong style="outline: 0px;"><span style="outline: 0px;letter-spacing: 0.034em;font-size: 20px;">3.单例模式</span></strong></p> <section data-mpa-template="t" mpa-from-tpl="t" style="outline: 0px;"> <section data-mpa-template="t" mpa-from-tpl="t" style="outline: 0px;"> <section data-color="rgb(252, 180, 43)" mpa-from-tpl="t" style="outline: 0px;"> <section mpa-from-tpl="t" style="outline: 0px;"> <section mpa-from-tpl="t" style="outline: 0px;"> <section mpa-from-tpl="t" style="outline: 0px;display: inline-block;"> <section mpa-from-tpl="t" style="margin-top: -10px;outline: 0px;background-color: rgb(252, 180, 43);height: 8px;color: rgb(255, 255, 255);"> <br mpa-from-tpl="t" style="outline: 0px;"> </section> </section> </section> </section> </section> </section> </section> <ul class="list-paddingleft-1" style="outline: 0px;width: 557.438px;"> <li style="outline: 0px;font-size: 16px;"><p style="outline: 0px;"><span style="outline: 0px;letter-spacing: 0px;">Spring依赖注入Bean实例默认是单例的。<br style="outline: 0px;"></span></p></li> <li style="outline: 0px;font-size: 16px;"><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 26px;"><span style="outline: 0px;">Spring的依赖注入(包括lazy-init方式)都是发生在AbstractBeanFactory的getBean里。getBean的doGetBean方法调用getSingleton进行bean的创建。</span></p></li> <li style="outline: 0px;font-size: 16px;"><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 26px;"><span style="outline: 0px;">分析getSingleton()方法</span></p></li> </ul> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;outline: 0px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><code style="padding: 15px 16px 16px;outline: 0px;overflow-x: auto;color: rgb(221, 221, 221);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(39, 40, 34);border-radius: 5px;"><span style="outline: 0px;line-height: 26px;"><span style="outline: 0px;color: rgb(249, 38, 114);font-weight: bold;line-height: 26px;">public</span> Object <span style="outline: 0px;color: rgb(166, 226, 46);font-weight: bold;line-height: 26px;">getSingleton</span><span style="outline: 0px;line-height: 26px;">(String beanName)</span></span>{<br style="outline: 0px;"> <span style="outline: 0px;color: rgb(117, 113, 94);line-height: 26px;">//参数true设置标识允许早期依赖</span><br style="outline: 0px;"> <span style="outline: 0px;color: rgb(249, 38, 114);font-weight: bold;line-height: 26px;">return</span> getSingleton(beanName,<span style="outline: 0px;color: rgb(249, 38, 114);font-weight: bold;line-height: 26px;">true</span>);<br style="outline: 0px;">}<br style="outline: 0px;"><span style="outline: 0px;line-height: 26px;"><span style="outline: 0px;color: rgb(249, 38, 114);font-weight: bold;line-height: 26px;">protected</span> Object <span style="outline: 0px;color: rgb(166, 226, 46);font-weight: bold;line-height: 26px;">getSingleton</span><span style="outline: 0px;line-height: 26px;">(String beanName, <span style="outline: 0px;color: rgb(249, 38, 114);font-weight: bold;line-height: 26px;">boolean</span> allowEarlyReference)</span> </span>{<br style="outline: 0px;"> <span style="outline: 0px;color: rgb(117, 113, 94);line-height: 26px;">//检查缓存中是否存在实例</span><br style="outline: 0px;"> Object singletonObject = <span style="outline: 0px;color: rgb(249, 38, 114);font-weight: bold;line-height: 26px;">this</span>.singletonObjects.get(beanName);<br style="outline: 0px;"> <span style="outline: 0px;color: rgb(249, 38, 114);font-weight: bold;line-heig
作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="line-height: 1.6;word-break: break-word;text-align: left;padding: 5px;font-size: 16px;color: rgb(53, 53, 53);word-spacing: 0.8px;letter-spacing: 0.8px;border-radius: 16px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;" data-mpa-powered-by="yiban.io"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">今天这篇文章分享一下微服务架构路线,希望对大家有所帮助~</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">我为什么选择微服务架构?</span></h2> <p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.9495798319327731" data-s="300,640" data-type="png" data-w="476" style="height: auto !important;" src="/upload/cc68b6e99251e9f5a410e0e072775f94.png"></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;text-align: right;"><span style="color: rgb(255, 41, 65);"><strong>Java技术指南:<span style="font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.8px;text-align: right;word-spacing: 0.8px;text-wrap: wrap;">https://java-family.cn</span></strong></span><span style="color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.8px;text-align: right;word-spacing: 0.8px;text-wrap: wrap;"></span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">众所周知,单体应用程序,由于其种种不足,几乎不支持敏捷方法。如果你想为一个大型或复杂的业务创建一个软件项目,最好从微服务架构开始。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">微服务架构是一种灵活的架构,可以显著性地提高应用程序灵活性、可扩展性等。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">微服务架构路线</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">据我了解很多开发者,想知道他们应该如何开始微服务架构旅程,虽然有成千上万的资源可以使用,但是资源到处分散。我决定通过为微服务架构学习定义路线图,使这段旅程更加清晰。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">基本思路</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">基于微服务的架构通常有几个独立的单元,它们协同工作以接收和处理各种请求。这个复杂的某些部分可以是插件,这意味着在需要的情况下,你可以在不干扰应用程序的整体工作情况下, 新增一个新插件或删除一个插件。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">例如,如果你决定实现一个微服务架构,你应该熟悉应用程序生命周期中的各种关注点,如持久化、日志记录、监控、负载均衡、缓存等,此外你还应该知道哪些哪些工具比较好或哪些堆栈更适合你的应用程序。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">本文,我将从以下几个方面来介绍各种关注点</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;color: rgb(106, 115, 125);border-left-width: 2px;padding: 8px 10px 8px 15px;background: rgb(255, 249, 249);border-left-color: rgb(239, 112, 96);margin-top: 0px;margin-bottom: 20px;letter-spacing: 0.5444px;"> <ol style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);font-size: 16px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 它是什么? </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 我为什么要使用它? </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 哪些工具比较好? </section></li> </ol> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">请注意,我在哪些工具比较好部分提到了两三个哪些工具比较好,当然,我们还有很多其他哪些工具比较好,选择这些哪些工具比较好的标准是业务需求,受欢迎程度、性能、开源以及更新频率。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">再次注意,我们还有基于云的服务,这不在本文讨论的范围内。</p> <p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.5792922673656619" data-s="300,640" data-type="png" data-w="763" style="height: auto !important;" src="/upload/337c8b4bce9cd47243bd14c25de57bf3.png"></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">本文,我用上图作为架构图示例。这个图涉及到大部分微服务架构组件,虽然不是也很全面,但是微服务架构的标准模型。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">本文将会介绍微服务架构的关注点有:</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;color: rgb(106, 115, 125);border-left-width: 2px;padding: 8px 10px 8px 15px;background: rgb(255, 249, 249);border-left-color: rgb(239, 112, 96);margin-top: 0px;margin-bottom: 20px;letter-spacing: 0.5444px;"> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);font-size: 16px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> Docker </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 容器编排 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> Docker容器管理 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> API网关 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 负载均衡 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 服务发现 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 事件总线 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 日志记录 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 监控和警报 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 分布式追踪 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 数据持久化 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 缓存 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 云供应商 </section></li> </ul> </blockquote> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">Docker</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">它是什么:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">Docker 是一个开源平台,用于容器化你的应用程序,其中包含你的应用程序在各种环境中运行所需的类库和依赖项。在 Docker 的帮助下,开发团队能够将应用程序打包到容器中。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">我为什么要使用它:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">实际上,Docker 是容器化应用程序的哪些工具比较好之一,你也可以在不使用 Docker 的情况下创建容器,Docker 的真正好处是使这个过程更容易、更安全、更简单。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">哪些工具比较好:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">Docker</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">容器编排</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">它是什么:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">在容器化应用程序后,你将需要一些哪些工具比较好来管理容器化应用程序,以执行一些手动和自动操作,例如水平扩展。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">我为什么要使用它:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">这些哪些工具比较好为你的应用程序管理提供一些服务,例如自动负载均衡,保证服务的高可用性。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">这种服务是通过定义多个管理器节点来完成的,如果一个节点管理器出现任何故障,其他管理器可以保持应用程序服务可用。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">哪些工具比较好:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">Kubernetes or K8s,Docker Swarm</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">Docker 容器管理</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">它是什么:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">管理 Docker 环境、配置、安全等。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">我为什么要使用它:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">为用户提供了一个基于 GUI 的Docker 容器管理,可以使他们不必处理不舒服的 CLI。这些工具也为开发人员提供了丰富的 UI 来构建和发布他们的镜像,还可以通过提供简化的用户界面来更轻松地执行一些操作任务,例如服务水平扩展。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">哪些工具比较好:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">Portainer , DockStation, Kitematic,Rancher</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">API网关</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">它是什么:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">API 网关可以被视为一种充当你的应用程序服务和不同客户端之间的中间件。API 网关可以管理许多事情,例如:</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;color: rgb(106, 115, 125);border-left-width: 2px;padding: 8px 10px 8px 15px;background: rgb(255, 249, 249);border-left-color: rgb(239, 112, 96);margin-top: 0px;margin-bottom: 20px;letter-spacing: 0.5444px;"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(53, 53, 53);font-size: 16px;margin-right: 10px;margin-left: 10px;">Routing :网关接收所有 API 请求并将它们转发到目标服务。</p> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(53, 53, 53);font-size: 16px;margin-right: 10px;margin-left: 10px;">Logging :你将能够在一处记录所有请求。</p> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(53, 53, 53);font-size: 16px;margin-right: 10px;margin-left: 10px;">Authorization: 检查你作为用户是否有资格访问该服务,如果没有,可以拒绝该请求</p> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(53, 53, 53);font-size: 16px;margin-right: 10px;margin-left: 10px;">Performance profiling:你可以估计每个请求的执行时间并检查你的应用程序瓶颈。</p> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(53, 53, 53);font-size: 16px;margin-right: 10px;margin-left: 10px;">Caching:通过在网关级别处理缓存,你将消除服务上的大量流量。</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">事实上,它是作为一个反向代理工作的,客户端只需要知道你的网关,应用服务就可以隐藏起来,不直接向其他系统暴露。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">我为什么要使用它:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">如果没有 API 网关,你可能需要在每个服务中做一些横切关注点,例如,如果你想记录服务的请求和响应。此外,如果你的应用程序由多个服务组成,你的客户端需要知道每个服务地址,并且在更改服务地址的情况下,应该更新多个地方。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">哪些工具比较好:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">Kong,Ocelot</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">负载均衡</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">它是什么:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">我们选择微服务架构最重要的原因是可扩展性,这意味着我们将能够通过运行更多服务实例来处理更多请求,但问题是,哪个实例应该接收请求,或客户端如何知道哪个服务实例应该处理请求?</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">这些问题的答案是负载均衡。负载均衡是高可用网络基础架构的关键组件,通常用于将工作负载分布到多个服务器来提高网站、应用、数据库或其他服务的性能和可靠性。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">我为什么要使用它:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">为了扩展你的独立服务,你需要运行多个服务实例。使用负载均衡器,客户端不需要知道服务的正确实例。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">哪些工具比较好:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">Traefik , NGINX,Seesaw</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">服务发现</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">它是什么:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">随着你的应用服务的数量越来越多,服务需要知道彼此的服务实例地址,但是在有很多服务的大型应用中,这是无法处理的。因此我们需要服务发现,它负责提供应用程序中所有组件的地址,它们可以轻松地向服务发现系统发送请求并获取可用的服务实例地址。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">我为什么要使用它:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">当你的应用程序中可以有多个服务时,服务发现对于你的应用程序来说是必不可少的。你的应用服务不需要知道每个服务实例地址,这意味着服务发现为你铺平了道路。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">哪些工具比较好:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">Consul,Zookeeper,Eureka,etcd和Keepalived</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">事件总线</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">它是什么:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">在微服务架构模式中,你将使用两种不同类型的通信,同步通信以及异步通信。同步通信是指服务之间通过 HTTP 或 GRPC 相互调用。异步通信意味着服务通过消息总线或事件总线相互交互,这意味着服务之间没有直接连接。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">你的架构可以同时使用两种通信方式,例如在在线商店示例中,你可以在订单注册时发送消息,并且订阅了特定频道的服务将收到该消息。但有时你可能需要一些实时的查询,例如,你需要知道一个物品的数量,你可能会在服务之间使用 GRPC 或 HTTP 调用来获取响应。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">我为什么要使用它:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">如果你想要一个包含多个服务的可扩展应用程序,你将遵循的原则之一是创建松散耦合的服务,这些服务通过事件总线相互交互。此外,如果你需要创建一个能够插入新服务以接收一系列特定消息的应用程序,则需要使用事件总线。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">哪些工具比较好:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">RabbitMQ,Kafka</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">日志记录</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">它是什么:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">使用微服务架构模式时,最好将服务日志集中起来。这些日志将用于调试问题或根据其类型聚合日志以供分析使用。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">我为什么要使用它:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">系统调试时,如果没有提前集中在一个地方收集服务日志,你可能会遇到困难。你还可以将与特定请求相关的日志与唯一的相关 ID 关联。这意味着与请求相关的不同服务中的所有日志都可以通过此关联 ID 访问。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">哪些工具比较好:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">Elastic Logstash</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">监控和警报</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">它是什么:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">在微服务架构中,如果你想要一个可靠的应用程序或服务,你必须监控应用程序的功能、性能、通信和任何其他方面,以实现一个负责任的应用程序。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">我为什么要使用它:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">你需要监控整体功能和服务健康状况,还需要监控性能瓶颈,并准备解决故障的计划。通过在关键点定义服务的早期警报来减少服务的停机时间,从而优化用户体验。当负载较重时等,可以监控服务的整体资源消耗。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">哪些工具比较好:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">Prometheus , Kibana,Graphana</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">分布式追踪</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">它是什么:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">调试始终是开发人员最关心的问题之一,因为你都有跟踪或调试单体引用程序的经验。那是非常直接和容易,但是在微服务架构上,因为一个请求可能会通过不同的服务,这使得调试和跟踪变得困难,因为服务不在一个地方,所以分布式追踪工具将会有所帮助。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">我为什么要使用它:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">如果没有分布式跟踪哪些工具比较好,通过不同的服务跟踪你的请求会令人沮丧或不可能。你可以借助用于演示请求流的丰富 UI 轻松跟踪请求和事件。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">哪些工具比较好:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">OpenTelemetry , Jeager,Zipkin</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">数据持久化</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">它是什么:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">在大多数系统中,我们需要持久化数据,将应用程序的数据写入具有不同结构的物理文件中,以便数据用于进一步的处理或报告。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">我为什么要使用它:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">在单体应用程序中,我们曾经有一种或两种不同的持久性类型,大多数单体应用程序使用关系数据库,如 SQL Server、Oracle、MySQL。但是在微服务架构中,我们应该遵循“DataBase Per Service”模式,这意味着保持每个微服务的持久数据对该服务是私有的,并且只能通过其 API 访问。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">对于不同的用途和场景,你将拥有不同的数据库。例如,数据展示服务可能会使用像 ElasticSearch 或 MongoDB 这样的 NoSQL 数据库,因为它们使用文档基础结构,这意味着这些数据库中持久化数据的结构与关系数据库不同,更适用于具有读多写少的服务。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">另一方面,在某些微服务中,你可能需要 Oracle 或 SQL SERVER 等关系数据库,或者你可能还需要一些支持图结构或键值结构的数据库。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">所以,在微服务架构中,根据服务的使命,你会需要不同类型的数据库。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">哪些工具比较好:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">关系数据库或 RDBMS : PostgreSQL, MySQL, SQL SERVRE, Oracle</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">NoSQL 数据库 : MongoDB, Cassandra,Elasticsearch</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">缓存</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">它是什么:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">缓存减少了微服务架构的服务到服务通信的延迟。缓存是高速数据存储层。当从缓存中请求数据时,它的速度比访问硬盘中的数据要快。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">我为什么要使用它:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">在微服务架构中,有许多策略可以通过这些方式实现缓存。考虑以下:</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;color: rgb(106, 115, 125);border-left-width: 2px;padding: 8px 10px 8px 15px;background: rgb(255, 249, 249);border-left-color: rgb(239, 112, 96);margin-top: 0px;margin-bottom: 20px;letter-spacing: 0.5444px;"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(53, 53, 53);font-size: 16px;margin-right: 10px;margin-left: 10px;">1:嵌入式缓存(分布式和非分布式)</p> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(53, 53, 53);font-size: 16px;margin-right: 10px;margin-left: 10px;">2:客户端-服务器缓存(分布式)</p> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(53, 53, 53);font-size: 16px;margin-right: 10px;margin-left: 10px;">3:反向代理缓存(Sidecar)</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">为了减少延迟,可以在不同的层中实现缓存。此外,你还可以实现分布式缓存,它可以被多个微服务访问。它们还有不同的用途,比如限流,限流的目的是通过对并发访问/请求进行限速或者一个时间窗口内的的请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务。。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">哪些工具比较好</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">Redis (Remote Dictionary Server), Apache Ignite,Hazelcast IMDG</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">云供应商</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">它是什么:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">云服务提供商是一个第三方公司,提供基于云的平台,基础设施,应用程序或存储服务。就像房主为电力或天然气等公用事业付费一样,公司通常只需根据业务需求为他们使用的云服务数量付费。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">云提供商最重要的类别:</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;color: rgb(106, 115, 125);border-left-width: 2px;padding: 8px 10px 8px 15px;background: rgb(255, 249, 249);border-left-color: rgb(239, 112, 96);margin-top: 0px;margin-bottom: 20px;letter-spacing: 0.5444px;"> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);font-size: 16px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 软件即服务 (SaaS)。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 平台即服务 (PaaS)。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 基础设施即服务 (IaaS)。 </section></li> </ul> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">我为什么要使用它</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">使用云计算服务的一个好处是,公司可以避免搭建和维护自己的 IT 基础设施的前期成本和复杂性,而只需在使用时为所用的东西付费。今天,公司可以租用从应用程序到存储的任何东西,而不是拥有自己的计算基础设施或数据中心。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">哪些工具比较好</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">Amazon Web Services (AWS), Microsoft Azure, Google Cloud,Alibaba Cloud</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">结论</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">在本文中,我试图展示一个与微服务架构模式相关的路线图。如果你想从头开始实现微服务架构或将单体架构迁移到微服务架构,你将需要了解这些概念。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">除了这些概念之外,我们还有其他概念,如服务网格、缓存、持久性,它们可能是本路线图的一部分,但为了简单起见,我故意没有提及它们。</p> </section>
作者:微信小助手
<section class="mp_profile_iframe_wrp" data-mpa-powered-by="yiban.io"> <mp-common-profile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-id="MzU2MTIyNDUwMA==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/EO58xpw5UMOQ7SLUFBoTAic22Pd63GqfXZibppZSGia2DsCllsnZrhZZqFN0ucxmztqP0icicOEiaQKAIAvnF71lqT4w/0?wx_fmt=png" data-nickname="前端充电宝" data-alias="FE-Charge" data-signature="掘金LV8作者,坚持原创。分享前端技术文章、学习资料、面试经验、热点资讯,开启前端进阶之旅!" data-from="0" data-is_biz_ban="0"></mp-common-profile> </section> <section data-tool="mdx editor" data-website="https://editor.runjs.cool/"> <section data-tool="mdx 编辑器" data-website="https://editor.runjs.cool/" style="font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;color: rgb(30, 41, 59);line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-size: 16px;"> <p data-line="1" style="margin-top: 16px;margin-bottom: 16px;line-height: 26px;">有时候不希望用户去调试或复制前端代码,那该如何禁止用户调试前端代码呢?今天就来分享一个开源的前端工具:<span style="padding: 2px 4px;color: rgb(14, 165, 233);font-weight: 600;border-bottom: 1px solid rgb(14, 165, 233);">Disable Devtool</span>,一行代码禁用 Web 开发者工具!</p> <h2 data-line="2" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: rgb(17, 24, 39);font-size: 22px;padding-bottom: 4px;border-bottom: 1px solid rgba(236, 236, 236, 0.5);">功能简介</h2> <p data-line="3" style="margin-top: 16px;margin-bottom: 16px;line-height: 26px;">disable-devtool 可以禁用一切可以进入开发者工具的方法,阻止通过开发者工具进行的“代码抓取”。</p> <p data-line="5" style="margin-top: 16px;margin-bottom: 16px;line-height: 26px;">它具有以下特点:</p> <ul data-line="20" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-1"> <li><p>支持可配置是否禁用右键菜单</p></li> <li><p>取消 f12 和 ctrl+shift+i 等快捷键</p></li> <li><p>支持识别从浏览器菜单栏打开开发者工具并关闭当前页面</p></li> <li><p>开发者可以绕过拒绝(url参数使用tk配合md5加密)</p></li> <li><p>多种监测模式,支持几乎所有浏览器(IE,360,QQ浏览器,FireFox,Chrome,Edge...)</p></li> <li><p>高度可配置、使用极简、体积紧凑</p></li> <li><p>支持npm引用和脚本标签引用(属性配置)</p></li> <li><p>识别真移动端与浏览器开发者工具设置插件格式化的移动端,为移动端节省性能</p></li> <li><p>支持识别开发者工具关闭事件</p></li> <li><p>支持可配置是否取消选择、复制、剪切、粘贴功能</p></li> <li><p>支持识别eruda和vconsole调试工具</p></li> <li><p>支持挂起和恢复支架工作</p></li> <li><p>支持配置ignore属性,默认自定义控制是否启用</p></li> <li><p>支持配置 iframe 中所有父页面的开发者工具禁用</p></li> </ul> <h2 data-line="21" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: rgb(17, 24, 39);font-size: 22px;padding-bottom: 4px;border-bottom: 1px solid rgba(236, 236, 236, 0.5);">基本使用</h2> <p data-line="22" style="margin-top: 16px;margin-bottom: 16px;line-height: 26px;">推荐使用 npm 进行安装(使用<code data-line="22" style="font-size: 14px;word-break: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(14, 165, 233);background-color: rgba(15, 164, 233, 0.08);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;max-width: unset;">script</code>脚本会被代理单独拦截掉从而无法执行),安装 disable-devtool:</p> <pre data-line="25" style="color: rgb(248, 248, 242);font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;word-spacing: normal;word-break: normal;overflow-wrap: normal;line-height: 1.5;tab-size: 4;hyphens: none;margin-top: 0.5em;margin-bottom: 0.5em;overflow: auto;border-radius: 0.3em;background: rgb(43, 43, 43);"> <section style="display: flex;align-items: center;padding: 12px;"> <span style="display: inline-block;align-items: center;width: 9px;height: 9px;margin-right: 8px;padding: 1px;border-radius: 50%;background-color: #ff605c;"></span> <span style="display: inline-block;align-items: center;width: 9px;height: 9px;margin-right: 8px;padding: 1px;border-radius: 50%;background-color: #ffbd44;"></span> <span style="display: inline-block;align-items: center;width: 9px;height: 9px;margin-right: 8px;padding: 1px;border-radius: 50%;background-color: #00ca4e;"></span> </section><code data-line="25" style="float: left;background: none;font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;word-spacing: normal;overflow-wrap: normal;line-height: 1.5;tab-size: 4;hyphens: none;min-width: 100%;font-size: 12px;word-break: normal;display: block;flex: 0 0 auto;padding-right: 12px;padding-bottom: 12px;padding-left: 12px;max-width: unset;"><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);">npm i disable<span style="color: #00e0e0;">-</span>devtool<br></span></code></pre> <p data-line="26" style="margin-top: 16px;margin-bottom: 16px;line-height: 26px;">基本使用:</p> <pre data-line="31" style="color: rgb(248, 248, 242);font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;word-spacing: normal;word-break: normal;overflow-wrap: normal;line-height: 1.5;tab-size: 4;hyphens: none;margin-top: 0.5em;margin-bottom: 0.5em;overflow: auto;border-radius: 0.3em;background: rgb(43, 43, 43);"> <section style="display: flex;align-items: center;padding: 12px;"> <span style="display: inline-block;align-items: center;width: 9px;height: 9px;margin-right: 8px;padding: 1px;border-radius: 50%;background-color: #ff605c;"></span> <span style="display: inline-block;align-items: center;width: 9px;height: 9px;margin-right: 8px;padding: 1px;border-radius: 50%;background-color: #ffbd44;"></span> <span style="display: inline-block;align-items: center;width: 9px;height: 9px;margin-right: 8px;padding: 1px;border-radius: 50%;background-color: #00ca4e;"></span> </section><code data-line="31" style="float: left;background: none;font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;word-spacing: normal;overflow-wrap: normal;line-height: 1.5;tab-size: 4;hyphens: none;min-width: 100%;font-size: 12px;word-break: normal;display: block;flex: 0 0 auto;padding-right: 12px;padding-bottom: 12px;padding-left: 12px;max-width: unset;"><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"><span style="color: #00e0e0;">import</span> DisableDevtool <span style="color: #00e0e0;">from</span> <span style="color: #abe338;">'disable-devtool'</span><span style="color: #fefefe;">;</span><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"><span style="color: #ffd700;">DisableDevtool</span><span style="color: #fefefe;">(</span>options<span style="color: #fefefe;">)</span><span style="color: #fefefe;">;</span><br></span></code></pre> <p data-line="32" style="margin-top: 16px;margin-bottom: 16px;line-height: 26px;">这里的 options 就是配置项。可配置参数如下:</p> <pre data-line="54" style="color: rgb(248, 248, 242);font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;word-spacing: normal;word-break: normal;overflow-wrap: normal;line-height: 1.5;tab-size: 4;hyphens: none;margin-top: 0.5em;margin-bottom: 0.5em;overflow: auto;border-radius: 0.3em;background: rgb(43, 43, 43);"> <section style="display: flex;align-items: center;padding: 12px;"> <span style="display: inline-block;align-items: center;width: 9px;height: 9px;margin-right: 8px;padding: 1px;border-radius: 50%;background-color: #ff605c;"></span> <span style="display: inline-block;align-items: center;width: 9px;height: 9px;margin-right: 8px;padding: 1px;border-radius: 50%;background-color: #ffbd44;"></span> <span style="display: inline-block;align-items: center;width: 9px;height: 9px;margin-right: 8px;padding: 1px;border-radius: 50%;background-color: #00ca4e;"></span> </section><code data-line="54" style="float: left;background: none;font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;word-spacing: normal;overflow-wrap: normal;line-height: 1.5;tab-size: 4;hyphens: none;min-width: 100%;font-size: 12px;word-break: normal;display: block;flex: 0 0 auto;padding-right: 12px;padding-bottom: 12px;padding-left: 12px;max-width: unset;"><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"><span style="color: #00e0e0;">interface</span> IConfig <span style="color: #fefefe;">{</span><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"> md5<span style="color: #00e0e0;">?</span><span style="color: #00e0e0;">:</span> <span style="color: #abe338;">string</span><span style="color: #fefefe;">;</span> <span style="color: #d4d0ab;">// 绕过禁用的md5值,详情见3.2,默认不启用绕过禁用</span><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"> url<span style="color: #00e0e0;">?</span><span style="color: #00e0e0;">:</span> <span style="color: #abe338;">string</span><span style="color: #fefefe;">;</span> <span style="color: #d4d0ab;">// 关闭页面失败时的跳转页面,默认值为localhost</span><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"> tkName<span style="color: #00e0e0;">?</span><span style="color: #00e0e0;">:</span> <span style="color: #abe338;">string</span><span style="color: #fefefe;">;</span> <span style="color: #d4d0ab;">// 绕过禁用时的url参数名称,默认为 ddtk</span><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"> ondevtoolopen<span style="color: #00e0e0;">?</span><span style="color: #fefefe;">(</span>type<span style="color: #00e0e0;">:</span> DetectorType<span style="color: #fefefe;">,</span> next<span style="color: #00e0e0;">:</span> <span style="color: #abe338;">Function</span><span style="color: #fefefe;">)</span><span style="color: #00e0e0;">:</span> <span style="color: #00e0e0;">void</span><span style="color: #fefefe;">;</span> <span style="color: #d4d0ab;">// 开发者面板打开的回调,启用时url参数无效,type 为监测模式,详见3.5, next函数是关闭当前窗口</span><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"> ondevtoolclose<span style="color: #00e0e0;">?</span><span style="color: #fefefe;">(</span><span style="color: #fefefe;">)</span><span style="color: #00e0e0;">:</span> <span style="color: #00e0e0;">void</span><span style="color: #fefefe;">;</span> <span style="color: #d4d0ab;">// 开发者面板关闭的回调</span><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"> interval<span style="color: #00e0e0;">?</span><span style="color: #00e0e0;">:</span> <span style="color: #abe338;">number</span><span style="color: #fefefe;">;</span> <span style="color: #d4d0ab;">// 定时器的时间间隔 默认200ms</span><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"> disableMenu<span style="color: #00e0e0;">?</span><span style="color: #00e0e0;">:</span> <span style="color: #abe338;">boolean</span><span style="color: #fefefe;">;</span> <span style="color: #d4d0ab;">// 是否禁用右键菜单 默认为true</span><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"> stopIntervalTime<span style="color: #00e0e0;">?</span><span style="color: #00e0e0;">:</span> <span style="color: #abe338;">number</span><span style="color: #fefefe;">;</span> <span style="color: #d4d0ab;">// 在移动端时取消监视的等待时长</span><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"> clearIntervalWhenDevOpenTrigger<span style="color: #00e0e0;">?</span><span style="color: #00e0e0;">:</span> <span style="color: #abe338;">boolean</span><span style="color: #fefefe;">;</span> <span style="color: #d4d0ab;">// 是否在触发之后停止监控 默认为false, 在使用ondevtoolclose时该参数无效</span><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"> detectors<span style="color: #00e0e0;">?</span><span style="color: #00e0e0;">:</span> <span style="color: #abe338;">Array</span><span style="color: #00e0e0;"><</span>DetectorType<span style="color: #00e0e0;">></span><span style="color: #fefefe;">;</span> <span style="color: #d4d0ab;">// 启用的检测器 检测器详情见 3.5 默认为全部,建议使用全部</span><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"> clearLog<span style="color: #00e0e0;">?</span><span style="color: #00e0e0;">:</span> <span style="color: #abe338;">boolean</span><span style="color: #fefefe;">;</span> <span style="color: #d4d0ab;">// 是否每次都清除log</span><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"> disableSelect<span style="color: #00e0e0;">?</span><span style="color: #00e0e0;">:</span> <span style="color: #abe338;">boolean</span><span style="color: #fefefe;">;</span> <span style="color: #d4d0ab;">// 是否禁用选择文本 默认为false</span><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"> disableCopy<span style="color: #00e0e0;">?</span><span style="color: #00e0e0;">:</span> <span style="color: #abe338;">boolean</span><span style="color: #fefefe;">;</span> <span style="color: #d4d0ab;">// 是否禁用复制 默认为false</span><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"> disableCut<span style="color: #00e0e0;">?</span><span style="color: #00e0e0;">:</span> <span style="color: #abe338;">boolean</span><span style="color: #fefefe;">;</span> <span style="color: #d4d0ab;">// 是否禁用剪切 默认为false</span><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"> disablePaste<span style="color: #00e0e0;">:</span> <span style="color: #abe338;">boolean</span><span style="color: #fefefe;">;</span> <span style="color: #d4d0ab;">// 是否禁用粘贴 默认为false</span><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"> ignore<span style="color: #00e0e0;">?</span><span style="color: #00e0e0;">:</span> <span style="color: #fefefe;">(</span><span style="color: #abe338;">string</span><span style="color: #00e0e0;">|</span>RegExp<span style="color: #fefefe;">)</span><span style="color: #fefefe;">[</span><span style="color: #fefefe;">]</span> <span style="color: #00e0e0;">|</span> <span style="color: #00e0e0;">null</span> <span style="color: #00e0e0;">|</span> <span style="color: #fefefe;">(</span><span style="color: #fefefe;">(</span><span style="color: #fefefe;">)</span><span style="color: #00e0e0;">=></span><span style="color: #abe338;">boolean</span><span style="color: #fefefe;">)</span><span style="color: #fefefe;">;</span> <span style="color: #d4d0ab;">// 某些情况忽略禁用</span><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"> disableIframeParents<span style="color: #00e0e0;">?</span><span style="color: #00e0e0;">:</span> <span style="color: #abe338;">boolean</span><span style="color: #fefefe;">;</span> <span style="color: #d4d0ab;">// iframe中是否禁用所有父窗口</span><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"> timeOutUrl<span style="color: #00e0e0;">?</span><span style="color: #00e0e0;">:</span> <span style="color: #d4d0ab;">// 关闭页面超时跳转的url;</span><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"><span style="color: #fefefe;">}</span><br></span></code></pre> <p data-line="55" style="margin-top: 16px;margin-bottom: 16px;line-height: 26px;">DisableDevtool 的返回值类型如下:</p> <pre data-line="61" style="color: rgb(248, 248, 242);font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;word-spacing: normal;word-break: normal;overflow-wrap: normal;line-height: 1.5;tab-size: 4;hyphens: none;margin-top: 0.5em;margin-bottom: 0.5em;overflow: auto;border-radius: 0.3em;background: rgb(43, 43, 43);"> <section style="display: flex;align-items: center;padding: 12px;"> <span style="display: inline-block;align-items: center;width: 9px;height: 9px;margin-right: 8px;padding: 1px;border-radius: 50%;background-color: #ff605c;"></span> <span style="display: inline-block;align-items: center;width: 9px;height: 9px;margin-right: 8px;padding: 1px;border-radius: 50%;background-color: #ffbd44;"></span> <span style="display: inline-block;align-items: center;width: 9px;height: 9px;margin-right: 8px;padding: 1px;border-radius: 50%;background-color: #00ca4e;"></span> </section><code data-line="61" style="float: left;background: none;font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;word-spacing: normal;overflow-wrap: normal;line-height: 1.5;tab-size: 4;hyphens: none;min-width: 100%;font-size: 12px;word-break: normal;display: block;flex: 0 0 auto;padding-right: 12px;padding-bottom: 12px;padding-left: 12px;max-width: unset;"><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"><span style="color: #00e0e0;">interface</span> IDDResult <span style="color: #fefefe;">{</span><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"> success<span style="color: #00e0e0;">:</span> <span style="color: #abe338;">boolean</span><span style="color: #fefefe;">;</span> <span style="color: #d4d0ab;">// 表示是否正常启用</span><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"> reason<span style="color: #00e0e0;">:</span> <span style="color: #abe338;">string</span><span style="color: #fefefe;">;</span> <span style="color: #d4d0ab;">// 未正常启用的原因</span><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"><span style="color: #fefefe;">}</span><br></span></code></pre> <p data-line="62" style="margin-top: 16px;margin-bottom: 16px;line-height: 26px;">Disable-Devtool 有以下监测模式,使用 <code data-line="62" style="font-size: 14px;word-break: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(14, 165, 233);background-color: rgba(15, 164, 233, 0.08);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;max-width: unset;">detectors</code> 定义:</p> <pre data-line="75" style="color: rgb(248, 248, 242);font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;word-spacing: normal;word-break: normal;overflow-wrap: normal;line-height: 1.5;tab-size: 4;hyphens: none;margin-top: 0.5em;margin-bottom: 0.5em;overflow: auto;border-radius: 0.3em;background: rgb(43, 43, 43);"> <section style="display: flex;align-items: center;padding: 12px;"> <span style="display: inline-block;align-items: center;width: 9px;height: 9px;margin-right: 8px;padding: 1px;border-radius: 50%;background-color: #ff605c;"></span> <span style="display: inline-block;align-items: center;width: 9px;height: 9px;margin-right: 8px;padding: 1px;border-radius: 50%;background-color: #ffbd44;"></span> <span style="display: inline-block;align-items: center;width: 9px;height: 9px;margin-right: 8px;padding: 1px;border-radius: 50%;background-color: #00ca4e;"></span> </section><code data-line="75" style="float: left;background: none;font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;word-spacing: normal;overflow-wrap: normal;line-height: 1.5;tab-size: 4;hyphens: none;min-width: 100%;font-size: 12px;word-break: normal;display: block;flex: 0 0 auto;padding-right: 12px;padding-bottom: 12px;padding-left: 12px;max-width: unset;"><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"><span style="color: #00e0e0;">enum</span> DetectorType <span style="color: #fefefe;">{</span><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"> Unknown <span style="color: #00e0e0;">=</span> <span style="color: #00e0e0;">-</span><span style="color: #00e0e0;">1</span><span style="color: #fefefe;">,</span><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"> RegToString <span style="color: #00e0e0;">=</span> <span style="color: #00e0e0;">0</span><span style="color: #fefefe;">,</span> <span style="color: #d4d0ab;">// 根据正则检测</span><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"> DefineId<span style="color: #fefefe;">,</span> <span style="color: #d4d0ab;">// 根据dom id检测</span><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"> Size<span style="color: #fefefe;">,</span> <span style="color: #d4d0ab;">// 根据窗口尺寸检测</span><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"> DateToString<span style="color: #fefefe;">,</span> <span style="color: #d4d0ab;">// 根据Date.toString 检测</span><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"> FuncToString<span style="color: #fefefe;">,</span> <span style="color: #d4d0ab;">// 根据Function.toString 检测</span><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"> Debugger<span style="color: #fefefe;">,</span> <span style="color: #d4d0ab;">// 根据断点检测,仅在ios chrome 真机情况下有效</span><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"> Performance<span style="color: #fefefe;">,</span> <span style="color: #d4d0ab;">// 根据log大数据性能检测</span><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"> DebugLib<span style="color: #fefefe;">,</span> <span style="color: #d4d0ab;">// 检测第三方调试工具 eruda 和 vconsole </span><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"><span style="color: #fefefe;">}</span><span style="color: #fefefe;">;</span><br></span></code></pre> <p data-line="76" style="margin-top: 16px;margin-bottom: 16px;line-height: 26px;"><code data-line="76" style="font-size: 14px;word-break: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(14, 165, 233);background-color: rgba(15, 164, 233, 0.08);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;max-width: unset;">ondevtoolopen</code> 事件的回调参数就是被触发的监测模式。可以在 <code data-line="76" style="font-size: 14px;word-break: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(14, 165, 233);background-color: rgba(15, 164, 233, 0.08);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;max-width: unset;">ondevtoolopen</code> 里执行业务逻辑,比如做数据上报、用户行为分析等:</p> <pre data-line="84" style="color: rgb(248, 248, 242);font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;word-spacing: normal;word-break: normal;overflow-wrap: normal;line-height: 1.5;tab-size: 4;hyphens: none;margin-top: 0.5em;margin-bottom: 0.5em;overflow: auto;border-radius: 0.3em;background: rgb(43, 43, 43);"> <section style="display: flex;align-items: center;padding: 12px;"> <span style="display: inline-block;align-items: center;width: 9px;height: 9px;margin-right: 8px;padding: 1px;border-radius: 50%;background-color: #ff605c;"></span> <span style="display: inline-block;align-items: center;width: 9px;height: 9px;margin-right: 8px;padding: 1px;border-radius: 50%;background-color: #ffbd44;"></span> <span style="display: inline-block;align-items: center;width: 9px;height: 9px;margin-right: 8px;padding: 1px;border-radius: 50%;background-color: #00ca4e;"></span> </section><code data-line="84" style="float: left;background: none;font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;word-spacing: normal;overflow-wrap: normal;line-height: 1.5;tab-size: 4;hyphens: none;min-width: 100%;font-size: 12px;word-break: normal;display: block;flex: 0 0 auto;padding-right: 12px;padding-bottom: 12px;padding-left: 12px;max-width: unset;"><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"><span style="color: #ffd700;">DisableDevtool</span><span style="color: #fefefe;">(</span><span style="color: #fefefe;">{</span><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"> <span style="color: #ffd700;">ondevtoolopen</span><span style="color: #fefefe;">(</span>type<span style="color: #fefefe;">,</span> next<span style="color: #fefefe;">)</span><span style="color: #fefefe;">{</span><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"> <span style="color: #ffd700;">alert</span><span style="color: #fefefe;">(</span><span style="color: #abe338;">'Devtool opened with type:'</span> <span style="color: #00e0e0;">+</span> type<span style="color: #fefefe;">)</span><span style="color: #fefefe;">;</span><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"> <span style="color: #ffd700;">next</span><span style="color: #fefefe;">(</span><span style="color: #fefefe;">)</span><span style="color: #fefefe;">;</span><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"> <span style="color: #fefefe;">}</span><br></span><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);"><span style="color: #fefefe;">}</span><span style="color: #fefefe;">)</span><span style="color: #fefefe;">;</span><br></span></code></pre> <p data-line="85" style="margin-top: 16px;margin-bottom: 16px;line-height: 26px;">那么问题来了,如果把 Devtools 禁用了,那如果线上应用出了问题,作为应用的开发者,也是无法调试的,怎么办呢?该工具的作者当然想到了这一点,它<strong data-line="85" style="color: rgb(17, 24, 39);">使用 key 与 md5 配合的方式使开发者可以在线上绕过禁用</strong>。</p> <p data-line="87" style="margin-top: 16px;margin-bottom: 16px;line-height: 26px;"><strong data-line="87" style="color: rgb(17, 24, 39);">使用流程</strong>:指定一个 key a(该值不要记录在代码中),使用 md5 加密得到一个值 b,将 b 作为 md5 参数传入,开发者在访问 url 的时候只需要带上url参数 <code data-line="87" style="font-size: 14px;word-break: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(14, 165, 233);background-color: rgba(15, 164, 233, 0.08);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;max-width: unset;">?ddtk=a</code>即可绕过禁用。</p> <p data-line="89" style="margin-top: 16px;margin-bottom: 16px;line-height: 26px;"><code data-line="89" style="font-size: 14px;word-break: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(14, 165, 233);background-color: rgba(15, 164, 233, 0.08);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;max-width: unset;">disableDevtool</code>对象暴露了 md5 方法,可供开发者加密时使用:</p> <pre data-line="92" style="color: rgb(248, 248, 242);font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;word-spacing: normal;word-break: normal;overflow-wrap: normal;line-height: 1.5;tab-size: 4;hyphens: none;margin-top: 0.5em;margin-bottom: 0.5em;overflow: auto;border-radius: 0.3em;background: rgb(43, 43, 43);"> <section style="display: flex;align-items: center;padding: 12px;"> <span style="display: inline-block;align-items: center;width: 9px;height: 9px;margin-right: 8px;padding: 1px;border-radius: 50%;background-color: #ff605c;"></span> <span style="display: inline-block;align-items: center;width: 9px;height: 9px;margin-right: 8px;padding: 1px;border-radius: 50%;background-color: #ffbd44;"></span> <span style="display: inline-block;align-items: center;width: 9px;height: 9px;margin-right: 8px;padding: 1px;border-radius: 50%;background-color: #00ca4e;"></span> </section><code data-line="92" style="float: left;background: none;font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;word-spacing: normal;overflow-wrap: normal;line-height: 1.5;tab-size: 4;hyphens: none;min-width: 100%;font-size: 12px;word-break: normal;display: block;flex: 0 0 auto;padding-right: 12px;padding-bottom: 12px;padding-left: 12px;max-width: unset;"><span style="display: block;padding-left: 16px;padding-right: 16px;margin-left: -16px;margin-right: -16px;border-left: 4px solid rgba(0, 0, 0, 0);">DisableDevtool<span style="color: #fefefe;">.</span><span style="color: #ffd700;">md5</span><span style="color: #fefefe;">(</span><span style="color: #abe338;">'xxx'</span><span style="color: #fefefe;">)</span><span style="color: #fefefe;">;</span><br></span></code></pre> </section> </section> <p><br></p> <p style="display: none;"> <mp-style-type data-value="3"></mp-style-type></p>
作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;text-align: left;line-height: 1.6;letter-spacing: 0.034em;color: rgb(63, 63, 63);font-size: 16px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;" data-mpa-powered-by="yiban.io"> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">用户登录之后,会返回一个用户的标识,之后带上这个标识请求别的接口,就能识别出该用户。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">标识登录状态的方案有两种: session 和 jwt。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">session 是通过 cookie 返回一个 id,关联服务端内存里保存的 session 对象,请求时服务端取出 cookie 里 id 对应的 session 对象,就可以拿到用户信息。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.362962962962963" src="/upload/807f5d81deb66d31c4b5c52e95a6e7f.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">jwt 不在服务端存储,会直接把用户信息放到 token 里返回,每次请求带上这个 token,服务端就能从中取出用户信息。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.40370370370370373" src="/upload/980cf232264ac47a4f8001a6f707ecf.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这个 token 一般是放在一个叫 authorization 的 header 里。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这两种方案一个服务端存储,通过 cookie 携带标识,一个在客户端存储,通过 header 携带标识。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">session 的方案默认不支持分布式,因为是保存在一台服务器的内存的,另一台服务器没有。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.5592592592592592" src="/upload/ad905f68d1a86721fb981938a3ad076a.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">jwt 的方案天然支持分布式,因为信息保存在 token 里,只要从中取出来就行。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.5712962962962963" src="/upload/6dfb94836b2d4107e32d91c9c86bd2b1.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">所以 jwt 的方案用的还是很多的。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">服务端把用户信息放入 token 里,设置一个过期时间,客户端请求的时候通过 authorization 的 header 携带 token,服务端验证通过,就可以从中取到用户信息。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">但是这样有个问题:</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">token 是有过期时间的,比如 3 天,那过期后再访问就需要重新登录了。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这样体验并不好。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">想想你在用某个 app 的时候,用着用着突然跳到登录页了,告诉你需要重新登录了。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">是不是体验很差?</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">所以要加上续签机制,也就是延长 token 过期时间。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">主流的方案是通过双 token,一个 access_token、一个 refresh_token。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">登录成功之后,返回这两个 token:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.3925925925925926" src="/upload/fd1fefb2c84a5e1663d164a43dae7068.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">访问接口时带上 access_token 访问:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.6198704103671706" src="/upload/ea9c67215427334605a1c4afe8e97754.png" data-type="png" data-w="926" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">当 access_token 过期时,通过 refresh_token 来刷新,拿到新的 access_token 和 refresh_token</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.34923664122137404" src="/upload/8e6be7573f2f06724e3ce6470105bf7f.png" data-type="png" data-w="1048" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这里的 access_token 就是我们之前的 token。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">为什么多了个 refresh_token 就能简化呢?</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">因为如果你重新登录,是不是需要再填一遍用户名密码?而有了 refresh_token 之后,只要带上这个 token 就能标识用户,不需要传用户名密码就能拿到新 token。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">而 access_token 一般过期时间设置的比较短,比如 30 分钟,refresh_token 设置的过期时间比较长,比如 7 天。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这样,只要你 7 天内访问一次,就能刷新 token,再续 7 天,一直不需要登录。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">但如果你超过 7 天没访问,那 refresh_token 也过期了,就需要重新登录了。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">想想你常用的 APP,是不是没再重新登录过?</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">而不常用的 APP,再次打开是不是就又要重新登录了?</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这种一般都是双 token 做的。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">知道了什么是双 token,以及它解决的问题,我们来实现一下。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">新建个 nest 项目:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/WAibKjHvK5nF8zePiaMqZPibiaJFLz2woOlia38s38Kiaic4MMvbjZk3sL0SicJEhdbsTPFM317YQAJVrDMAqXXl7B4CYhTKXUjnMeng/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"> npx nest new token-test<br></code></pre> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.7383966244725738" src="/upload/10942ba309694cbdabc20bacf6563a6e.png" data-type="png" data-w="948" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">进入项目,把它跑起来:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/WAibKjHvK5nF8zePiaMqZPibiaJFLz2woOlia38s38Kiaic4MMvbjZk3sL0SicJEhdbsTPFM317YQAJVrDMAqXXl7B4CYhTKXUjnMeng/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">npm run start:dev<br></code></pre> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">访问 http://localhost:3000 可以看到 hello world,代表服务跑成功了:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.3470790378006873" src="/upload/8eebb56c198786c84722a9d3a584f645.png" data-type="png" data-w="582" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">在 AppController 添加一个 login 的 post 接口:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.7724288840262582" src="/upload/cee51522a733de4677f98ef3d9a45dc6.png" data-type="png" data-w="914" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/WAibKjHvK5nF8zePiaMqZPibiaJFLz2woOlia38s38Kiaic4MMvbjZk3sL0SicJEhdbsTPFM317YQAJVrDMAqXXl7B4CYhTKXUjnMeng/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">@Post(<span style="color: #98c379;line-height: 26px;">'login'</span>)<br>login(@Body() userDto: UserDto) {<br> <span style="color: #e6c07b;line-height: 26px;">console</span>.log(userDto);<br> <span style="color: #c678dd;line-height: 26px;">return</span> <span style="color: #98c379;line-height: 26px;">'success'</span>;<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这里通过 @Body 取出请求体的内容,设置到 dto 中。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">dto 是 data transfer object,数据传输对象,用来保存参数的。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">我们创建 src/user.dto.ts</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/WAibKjHvK5nF8zePiaMqZPibiaJFLz2woOlia38s38Kiaic4MMvbjZk3sL0SicJEhdbsTPFM317YQAJVrDMAqXXl7B4CYhTKXUjnMeng/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="color: #c678dd;line-height: 26px;">export</span> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">class</span> <span style="color: #e6c07b;line-height: 26px;">UserDto</span> </span>{<br> <span style="color: #d19a66;line-height: 26px;">username</span>: string;<br> password: string;<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">在 postman 里访问下这个接口:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="1.1339285714285714" src="/upload/c7af3db42c3a93f8b8b68a343d1103aa.png" data-type="png" data-w="672" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">返回了 success,服务端也打印了收到的参数:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.4879356568364611" src="/upload/3b461d98ff9f34df2039dc69b02b25e0.png" data-type="png" data-w="746" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">然后我们实现下登录逻辑:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.9111111111111111" src="/upload/9c34e3cee25191e64afeed66acd6b195.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这里我们就不连接数据库了,就是内置几个用户,匹配下信息。</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/WAibKjHvK5nF8zePiaMqZPibiaJFLz2woOlia38s38Kiaic4MMvbjZk3sL0SicJEhdbsTPFM317YQAJVrDMAqXXl7B4CYhTKXUjnMeng/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="color: #c678dd;line-height: 26px;">const</span> users = [<br> { <span style="color: #d19a66;line-height: 26px;">username</span>: <span style="color: #98c379;line-height: 26px;">'guang'</span>, <span style="color: #d19a66;line-height: 26px;">password</span>: <span style="color: #98c379;line-height: 26px;">'111111'</span>, <span style="color: #d19a66;line-height: 26px;">email</span>: <span style="color: #98c379;line-height: 26px;">'xxx@xxx.com'</span>},<br> { <span style="color: #d19a66;line-height: 26px;">username</span>: <span style="color: #98c379;line-height: 26px;">'dong'</span>, <span style="color: #d19a66;line-height: 26px;">password</span>: <span style="color: #98c379;line-height: 26px;">'222222'</span>, <span style="color: #d19a66;line-height: 26px;">email</span>: <span style="color: #98c379;line-height: 26px;">'yyy@yyy.com'</span>},<br>]<br><br>@Post(<span style="color: #98c379;line-height: 26px;">'login'</span>)<br>login(@Body() userDto: UserDto) {<br> <span style="color: #c678dd;line-height: 26px;">const</span> user = users.find(<span style="line-height: 26px;"><span style="line-height: 26px;">item</span> =></span> item.username === userDto.username);<br><br> <span style="color: #c678dd;line-height: 26px;">if</span>(!user) {<br> <span style="color: #c678dd;line-height: 26px;">throw</span> <span style="color: #c678dd;line-height: 26px;">new</span> BadRequestException(<span style="color: #98c379;line-height: 26px;">'用户不存在'</span>);<br> }<br><br> <span style="color: #c678dd;line-height: 26px;">if</span>(user.password !== userDto.password) {<br> <span style="color: #c678dd;line-height: 26px;">throw</span> <span style="color: #c678dd;line-height: 26px;">new</span> BadRequestException(<span style="color: #98c379;line-height: 26px;">"密码错误"</span>);<br> }<br><br> <span style="color: #c678dd;line-height: 26px;">return</span> {<br> <span style="color: #d19a66;line-height: 26px;">userInfo</span>: {<br> <span style="color: #d19a66;line-height: 26px;">username</span>: user.username,<br> <span style="color: #d19a66;line-height: 26px;">email</span>: user.email<br> },<br> <span style="color: #d19a66;line-height: 26px;">accessToken</span>: <span style="color: #98c379;line-height: 26px;">'xxx'</span>,<br> <span style="color: #d19a66;line-height: 26px;">refreshToken</span>: <span style="color: #98c379;line-height: 26px;">'yyy'</span><br> };<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">如果没找到,就返回用户不存在。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">找到了但是密码不对,就返回密码错误。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">否则返回用户信息和 token。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">测试下:</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">当 username 不存在时:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="1.270358306188925" src="/upload/92524ef40ff389d522048624df4214f8.png" data-type="png" data-w="614" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">当 password 不对时:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="1.249221183800623" src="/upload/cc11e7e7a42e2bd83e2aeb35f3b1e05.png" data-type="png" data-w="642" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">登录成功时:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="1.167979002624672" src="/upload/2250bf50307775c37805a1a0c5fe2aee.png" data-type="png" data-w="762" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">然后我们引入 jwt 模块来生成 token:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/WAibKjHvK5nF8zePiaMqZPibiaJFLz2woOlia38s38Kiaic4MMvbjZk3sL0SicJEhdbsTPFM317YQAJVrDMAqXXl7B4CYhTKXUjnMeng/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">npm install @nestjs/jwt<br></code></pre> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">在 AppModule 里注册下这个模块:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.75" src="/upload/32b08840d8950f702d7ff5bab67931b0.png" data-type="png" data-w="832" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/WAibKjHvK5nF8zePiaMqZPibiaJFLz2woOlia38s38Kiaic4MMvbjZk3sL0SicJEhdbsTPFM317YQAJVrDMAqXXl7B4CYhTKXUjnMeng/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display
作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="line-height: 1.6;word-break: break-word;text-align: left;padding: 5px;font-size: 16px;color: rgb(53, 53, 53);word-spacing: 0.8px;letter-spacing: 0.8px;border-radius: 16px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;" data-mpa-powered-by="yiban.io"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">在MyBatis的两万多行的框架源码中,使用了大量的设计模式对工程架构中的复杂场景进行解耦,这些设计模式的巧妙使用是整个框架的精华。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">经过整理,大概有以下设计模式,如图1所示。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img class="rich_pages wxw-img" data-ratio="0.30866425992779783" src="/upload/fdffb722c919fc0945dfd4e9d810ea4e.png" data-w="554" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> </figure> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;"><span style="color: rgb(248, 57, 41);">类型:创建型模式</span></span></h2> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;"><span style="color: rgb(248, 57, 41);">工厂模式</span></span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">SqlSessionFactory 的结构如图2所示。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img class="rich_pages wxw-img" data-ratio="0.6263537906137184" src="/upload/86c3d8357300dc671da32ff9f0f1c3e1.png" data-w="554" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">工厂模式:</span>简单工厂是一种创建型模式,在父类中提供一个创建对象的方法,允许子类决定实例对象的类型。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">场景介绍:</span>SqlSessionFactory 是获取会话的工厂,每次使用MyBatis 操作数据库时, 都会开启一个新的会话。在会话工厂的实现中,SqlSessionFactory 负责获取数据源环境配置信息、构建事务工厂和创建操作SQL 的执行器,最终返回会话实现类。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">同类设计:</span>SqlSessionFactory、ObjectFactory、MapperProxyFactory 和DataSourceFactory。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;"><span style="color: rgb(248, 57, 41);">单例模式</span></span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">Configuration 单例配置类的结构如图3所示。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img class="rich_pages wxw-img" data-ratio="0.6967509025270758" src="/upload/f0b02f526a1ca1c4e7c4de4fa9676019.png" data-w="554" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">单例模式:</span>是一种创建型模式,能够保证一个类只有一个实例,并且提供一个访问该实例的全局节点。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">场景介绍:</span>Configuration 是一个大单例,贯穿整个会话周期,所有的配置对象(如映射、缓存、入参、出参、拦截器、注册机和对象工厂等)都在Configuration 配置项中初始化, 并且随着SqlSessionFactoryBuilder 构建阶段完成实例化操作。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">同类场景:</span>ErrorContext、LogFactory 和Configuration。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;"><span style="color: rgb(248, 57, 41);">建造者模式</span></span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">ResultMap 建造者模式的结构如图4所示。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img class="rich_pages wxw-img" data-ratio="0.41155234657039713" src="/upload/2fcc053637c804686e6a804539cc789c.png" data-w="554" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">建造者模式:</span>使用多个简单的对象一步一步地构建成一个复杂的对象,提供了一种创建对象的最佳方式。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">场景介绍:</span>建造者模式在MyBatis 中使用了大量的XxxxBuilder,将XML 文件解析到各类对象的封装中,关注工众号:码猿技术专栏,回复关键词:1111 获取阿里内部Java性能调优手册!使用建造者及建造者助手完成对象的封装。它的核心目的是不希望把过多的关于对象的属性设置写到其他业务流程中,而是用建造者方式提供最佳的边界隔离。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">同类场景:</span>SqlSessionFactoryBuilder、XMLConfigBuilder、XMLMapperBuilder、XML StatementBuilder 和CacheBuilder。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;"><span style="color: rgb(248, 57, 41);">类型:结构型模式</span></span></h2> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;"><span style="color: rgb(248, 57, 41);">适配器模式</span></span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">日志实现类的结构如图5所示。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img class="rich_pages wxw-img" data-ratio="0.7942238267148014" src="/upload/bba72a58eb7d1b0997b93bbf5fa44457.png" data-w="554" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">适配器模式:</span>是一种结构型模式,能使接口不兼容的对象也可以相互合作。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">场景介绍:</span>正是因为有太多的日志框架,包括Log4j、Log4j2 和Slf4J 等,而这些日志框架的使用接口又各有差异,为了统一这些日志框架的接口,MyBatis 定义了一套统一的接口,为所有的其他日志框架的接口做相应的适配。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">同类场景:</span>主要集中在对Log 日志的适配上。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;"><span style="color: rgb(248, 57, 41);">代理模式</span></span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">代理模式的实现结构如图6所示。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img class="rich_pages wxw-img" data-ratio="0.4747292418772563" src="/upload/83ef9281976254854203e2c1d46bb527.png" data-w="554" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">代理模式:</span>是一种结构型模式,能够提供对象的替代品或占位符。代理控制元对象的访问,并且允许在将请求提交给对象前进行一些处理。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">场景介绍:</span>没有代理模式就不存在各类框架。就像MyBatis 中的MapperProxy 实现类, 代理工厂实现的功能就是完成DAO 接口的具体实现类的方法,配置的任何一个DAO 接口调用的CRUD 方法,都会被MapperProxy 接管,调用到方法执行器等,并返回最终的数据库执行结果。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">同类场景:</span>DriverProxy、Plugin、Invoker 和MapperProxy。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;"><span style="color: rgb(248, 57, 41);">组合模式</span></span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">解析节点类的结构如图7所示。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img class="rich_pages wxw-img" data-ratio="0.8971119133574007" src="/upload/c5a53e4b35d6704fb73cb24335c6b5df.png" data-w="554" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">组合模式:</span>是一种结构型模式,可以将对象组合成树形结构以表示“部分—整体” 的层次结构。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">场景介绍:</span>在MyBatis XML 动态的SQL 配置中,共提供了9 种标签(trim、where、set、foreach、if、choose、when、otherwise 和bind),使用者可以组合出各类场景的SQL 语句。而SqlNode 接口的实现就是每个组合结构中的规则节点,通过规则节点的组装,完成规则树组合模式的使用。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">同类场景:</span>主要体现在对各类SQL 标签的解析上,以实现SqlNode 接口的各个子类为主。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;"><span style="color: rgb(248, 57, 41);">装饰器模式</span></span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">二级缓存装饰器的实现结构如图8所示。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img class="rich_pages wxw-img" data-ratio="0.5451263537906137" src="/upload/665f0475f48c3e4a61b3b5b41c4bf3b8.png" data-w="554" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">装饰器模式:</span>是一种结构型设计模式,允许将对象放入包含行为的特殊封装对象中, 为元对象绑定新的行为。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">场景介绍:</span>MyBatis 的所有SQL 操作都是经过SqlSession 调用SimpleExecutor 完成的, 而一级缓存的操作也是在简单执行器中处理的。这里的二级缓存因为是基于一级缓存刷新的,所以在实现上,通过创建一个缓存执行器,包装简单执行器的处理逻辑,实现二级缓存操作。这里用到的就是装饰器模式,也叫俄罗斯套娃模式。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;"><span style="color: rgb(248, 57, 41);">类型:行为型模式</span></span></h2> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;"><span style="color: rgb(248, 57, 41);">模板模式</span></span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">SQL 执行模板模式如图9所示。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img class="rich_pages wxw-img" data-ratio="0.7003610108303249" src="/upload/f2006ae6336cf00c47afdce42521d213.png" data-w="554" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">模板模式:</span>是一种行为型模式,在超类中定义了一个算法的框架,允许子类在不修改结构的情况下重写算法的特定步骤。场景介绍:存在一系列可被标准定义的流程,并且流程的步骤大部分采用通用逻辑,只有一小部分是需要子类实现的,通常采用模板模式来定义这个标准的流程。就像MyBatis 的BaseExecutor 就是一个用于定义模板模式的抽象类,在这个类中把查询、修改的操作都定义为一套标准的流程。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">同类场景:</span>BaseExecutor、SimpleExecutor 和BaseTypeHandler。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;"><span style="color: rgb(248, 57, 41);">策略模式</span></span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">多类型处理器策略模式的结构如图10所示。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img class="rich_pages wxw-img" data-ratio="1.023465703971119" src="/upload/2639c4827d7722d9706f21196dbee9c0.png" data-w="554" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">策略模式:</span>是一种行为型模式,能定义一系列算法,并将每种算法分别放入独立的类中,从而使算法的对象能够互相替换。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">场景介绍:</span>在MyBatis 处理JDBC 执行后返回的结果时,需要按照不同的类型获取对应的值,这样就可以避免大量的if 判断。所以,这里基于TypeHandler 接口对每个参数类型分别做了自己的策略实现。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">同类场景:</span>PooledDataSource、UnpooledDataSource、BatchExecutor、ResuseExecutor、SimpleExector、CachingExecutor、LongTypeHandler、StringTypeHandler 和DateTypeHandler。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;"><span style="color: rgb(248, 57, 41);">迭代器模式</span></span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">拆解字段解析实现的结构如图11所示。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img class="rich_pages wxw-img" data-ratio="0.8194945848375451" src="/upload/4988c9849cc76ba526c82e42e4e36deb.png" data-w="554" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">迭代器模式:</span>是一种行为型模式,能在不暴露集合底层表现形式的情况下遍历集合中的所有元素。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">场景介绍:</span>PropertyTokenizer 用于MyBatis 的MetaObject 反射工具包下,用来解析对象关系的迭代操作。这个类在MyBatis 中使用得非常频繁,包括解析数据源配置信息并填充到数据源类上,同时参数的解析、对象的设置都会使用这个类。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">同类场景:</span>PropertyTokenizer。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;"><span style="color: rgb(248, 57, 41);">总结</span></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">通过梳理,MyBatis大约运用了10种左右设计模式。可以说,复杂且优秀的ORM 框架源码在设计和实现的过程中都会使用大量的设计模式。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">在解决复杂场景的问题时,需要采用分治、抽象的方法,运用设计模式和设计原则等相关知识,把问题合理切割为若干子问题,以便加以理解和解决。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">学习源码远不是只是为了应付面试,更重要的是学习优秀框架在复杂场景下的解决方案。通过学习这些优秀的方案技术,可以提高对技术设计和实现的理解,扩展编码思维,积累落地经验。只有经过这样长期的积累,我们才更有可能成为优秀的高级工程师和架构师。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;color: rgb(106, 115, 125);border-left-width: 2px;padding: 8px 10px 8px 15px;background: rgb(255, 249, 249);border-left-color: rgb(239, 112, 96);margin-top: 0px;margin-bottom: 20px;letter-spacing: 0.5444px;"></blockquote> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"></figure> </section>
作者:微信小助手
<section class="mp_profile_iframe_wrp" data-mpa-powered-by="yiban.io"> <mp-common-profile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-weui-theme="light" data-id="Mzk0NTI4Mzg1OQ==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/Xuvryiawbuz76mibDl3nErju9YY7fb1pPn90sshpB1O33q1QfF9BbpRHYbR0JWAu5DaOglKLqQWCFT2MgkFjOwaQ/0?wx_fmt=png" data-nickname="臻大虾" data-alias="Zhen_Daxia" data-signature="分享java后端技术干货,知道的越多,不知道的越多" data-from="0" data-is_biz_ban="0"></mp-common-profile> </section> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;"> <h1 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 1.8em;color: rgb(0, 150, 136);margin: 1.2em auto;text-align: center;border-bottom: 1px solid rgb(0, 150, 136);"><span style="display: none;"></span><span>前言</span><span></span></h1> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;">在平时写接口的时候,需要进行参数的校验,如果参数少的话,使用 if else 还可以,但是参数多的时候,要写一大堆 if else 校验,敲的太累也不优雅。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;">所以今天将介绍使用注解来进行参数校验,既方便,还优雅。</p> <h1 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 1.8em;color: rgb(0, 150, 136);margin: 1.2em auto;text-align: center;border-bottom: 1px solid rgb(0, 150, 136);"><span style="display: none;"></span><span>@valid 和@Validated 区别</span><span></span></h1> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;"><code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(0, 150, 136);">@Validation</code>对<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(0, 150, 136);">@Valid</code>进行了二次封装</p> <section data-tool="mdnice编辑器" style="overflow-x: auto;"> <table> <thead> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;"> <th style="text-align: left;border-top-width: 1px;border-top-color: rgb(0, 150, 136);border-right-color: rgb(0, 150, 136);border-left-color: rgb(0, 150, 136);background-color: rgb(0, 150, 136);color: rgb(248, 248, 248);border-bottom: 0px;min-width: 85px;">区别</th> <th style="text-align: left;border-top-width: 1px;border-top-color: rgb(0, 150, 136);border-right-color: rgb(0, 150, 136);border-left-color: rgb(0, 150, 136);background-color: rgb(0, 150, 136);color: rgb(248, 248, 248);border-bottom: 0px;min-width: 85px;">@valid</th> <th style="text-align: left;border-top-width: 1px;border-top-color: rgb(0, 150, 136);border-right-color: rgb(0, 150, 136);border-left-color: rgb(0, 150, 136);background-color: rgb(0, 150, 136);color: rgb(248, 248, 248);border-bottom: 0px;min-width: 85px;">@validate</th> </tr> </thead> <tbody style="border-width: 0px;border-style: initial;border-color: initial;"> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;"> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">提供者</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">spring-boot-starter-web 里面,springboot 项目自带</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">Spring 做得一个自定义注解,增强了分组功能</td> </tr> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: rgb(248, 248, 248);"> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">是否支持分组</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">不支持</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">支持,参数校验时,根据不同的分组采取不同的校验</td> </tr> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;"> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">使用位置</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">构造函数、方法、方法参数、成员属性</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">类、方法、方法参数,不能用于成员属性</td> </tr> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: rgb(248, 248, 248);"> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">嵌套校验</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">支持,因为可以在成员属性上使用</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">不支持</td> </tr> </tbody> </table> </section> <h1 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 1.8em;color: rgb(0, 150, 136);margin: 1.2em auto;text-align: center;border-bottom: 1px solid rgb(0, 150, 136);"><span style="display: none;"></span><span>常用注解</span><span></span></h1> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;text-align: justify;">除了@Null,@ NotNull,@ NotBlank,@NotEmpty 这四个外,其他所有的注解,传 null 时都会被当作有效处理</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;text-align: justify;">注解常用参数值:message(校验不通过反馈的信息)</p> </section></li> </ul> <section data-tool="mdnice编辑器" style="overflow-x: auto;"> <table> <thead> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;"> <th style="text-align: left;border-top-width: 1px;border-top-color: rgb(0, 150, 136);border-right-color: rgb(0, 150, 136);border-left-color: rgb(0, 150, 136);background-color: rgb(0, 150, 136);color: rgb(248, 248, 248);border-bottom: 0px;min-width: 85px;">注解</th> <th style="text-align: left;border-top-width: 1px;border-top-color: rgb(0, 150, 136);border-right-color: rgb(0, 150, 136);border-left-color: rgb(0, 150, 136);background-color: rgb(0, 150, 136);color: rgb(248, 248, 248);border-bottom: 0px;min-width: 85px;">验证的数据类型</th> <th style="text-align: left;border-top-width: 1px;border-top-color: rgb(0, 150, 136);border-right-color: rgb(0, 150, 136);border-left-color: rgb(0, 150, 136);background-color: rgb(0, 150, 136);color: rgb(248, 248, 248);border-bottom: 0px;min-width: 85px;">备注</th> </tr> </thead> <tbody style="border-width: 0px;border-style: initial;border-color: initial;"> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;"> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">Null</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">任意类型</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">参数值必须是 Null</td> </tr> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: rgb(248, 248, 248);"> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">NotNull</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">任意类型</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">参数值必须不是 Null</td> </tr> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;"> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">NotBlank</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">只能作用于字符串</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">字符串不能为 null,而且字符串长度必须大于 0,至少包含一个非空字符串</td> </tr> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: rgb(248, 248, 248);"> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">NotEmpty</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">CharSequence Collection Map Array</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">参数值不能为 null,且不能为空 (字符串长度必须大于 0,空字符串(“ ”)可以通过校验)</td> </tr> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;"> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">Size(min,max )</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">CharSequence Collection Map Array</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">字符串:字符串长度必须在指定的范围内 Collection:集合大小必须在指定的范围内 Map:map 的大小必须在指定的范围内 Array:数组长度必须在指定的范围内</td> </tr> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: rgb(248, 248, 248);"> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">Pattern(regexp)</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">字符串类型</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">验证字符串是否符合正则表达式</td> </tr> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;"> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">Min(value)</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">整型类型</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">参数值必须大于等于 最小值</td> </tr> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: rgb(248, 248, 248);"> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">Max(value)</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">整型类型</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">参数值必须小于等于 最大值</td> </tr> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;"> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">DecimalMin(value)</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">整型类型</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">参数值必须大于等于 最小值</td> </tr> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: rgb(248, 248, 248);"> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">DecimalMax(value)</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">整型类型</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">参数值必须小于等于 最大值</td> </tr> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;"> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">Positive</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">数字类型</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">参数值为正数</td> </tr> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: rgb(248, 248, 248);"> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">PositiveOrZero</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">数字类型</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">参数值为正数或 0</td> </tr> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;"> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">Negative</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">数字类型</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">参数值为负数</td> </tr> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: rgb(248, 248, 248);"> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">NegativeOrZero</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">数字类型</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">参数值为负数或 0</td> </tr> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;"> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">Digits(integer,fraction)</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">数字类型</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">参数值为数字,且最大长度不超过 integer 位,整数部分最高位不超过 fraction 位</td> </tr> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: rgb(248, 248, 248);"> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">AssertTrue</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">布尔类型</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">参数值必须为 true</td> </tr> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;"> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">AssertFalse</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">布尔类型</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">参数值必须为 false</td> </tr> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: rgb(248, 248, 248);"> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">Past</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">时间类型(Date)</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">参数值为时间,且必须小于 当前时间</td> </tr> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;"> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">PastOrPresent</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">时间类型(Date)</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">参数值为时间,且必须小于或等于 当前时间</td> </tr> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: rgb(248, 248, 248);"> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">Future</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">时间类型(Date)</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">参数值为时间,且必须大于 当前时间</td> </tr> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;"> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">FutureOrPresent</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">时间类型(Date)</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">参数值为时间,且必须大于或等于 当前日期</td> </tr> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: rgb(248, 248, 248);"> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">Email</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">字符串类型</td> <td style="border-color: rgb(0, 150, 136);min-width: 85px;">被注释的元素必须是电子邮箱地址</td> </tr> </tbody> </table> </section> <h1 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 1.8em;color: rgb(0, 150, 136);margin: 1.2em auto;text-align: center;border-bottom: 1px solid rgb(0, 150, 136);"><span style="display: none;"></span><span>校验场景</span><span></span></h1> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 22px;color: rgb(0, 150, 136);padding-left: 10px;margin: 1em auto;border-left: 3px solid rgb(0, 150, 136);"><span style="display: none;"></span><span>post 请求校验</span><span></span></h2> <h3 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 20px;margin: 0.6em auto;padding-left: 10px;border-left: 2px solid rgb(0, 150, 136);"><span style="display: none;"></span><span>对象属性校验</span><span style="display: none;"></span></h3> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 在入参对象的字段上添加校验注解,比如@Min </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 在请求对象前面添加注解@Valid </section></li> </ul> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/nibxxlib1VaPfcUVibsaVCdpT5SQY72cdbDEpQtw1lOols8Dskz7WPOFr8GfeCBXgJCfeZxACuHXJegadicO6icib3ib7N5j1aMiaEv5/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="color: #61aeee;line-height: 26px;">@Data</span><br><span style="color: #c678dd;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">class</span> <span style="color: #e6c07b;line-height: 26px;">User</span> </span>{<br> <span style="color: #61aeee;line-height: 26px;">@Min</span>(value = <span style="color: #d19a66;line-height: 26px;">10</span>,message = <span style="color: #98c379;line-height: 26px;">"年龄必须大于10岁"</span>)<br> <span style="color: #c678dd;line-height: 26px;">private</span> Integer age;<br>}<br><br><span style="color: #61aeee;line-height: 26px;">@PostMapping</span>(<span style="color: #98c379;line-height: 26px;">"checkBodyParam"</span>)<br><span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span> String <span style="color: #61aeee;line-height: 26px;">checkBodyParam</span><span style="line-height: 26px;">(@RequestBody @Valid User user)</span></span>{<br> <span style="color: #c678dd;line-height: 26px;">return</span> <span style="color: #98c379;line-height: 26px;">"ok"</span>;<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;">当 age=2 时,校验不通过,提示年龄必须大于 10 岁</p> <p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.43611404435058077" data-s="300,640" src="/upload/648d453b33e5964d1900535daab4ce92.png" data-type="png" data-w="1894" style=""></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"> image-20230727115321573 </figcaption> </figure> <h3 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 20px;margin: 0.6em auto;padding-left: 10px;border-left: 2px solid rgb(0, 150, 136);"><span style="display: none;"></span><span>嵌套属性校验</span><span style="display: none;"></span></h3> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;text-align: justify;">在嵌套对象上添加注解 valid</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;text-align: justify;">在请求对象前面添加注解 valid</p> </section></li> </ul> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/nibxxlib1VaPfcUVibsaVCdpT5SQY72cdbDEpQtw1lOols8Dskz7WPOFr8GfeCBXgJCfeZxACuHXJegadicO6icib3ib7N5j1aMiaEv5/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="color: #61aeee;line-height: 26px;">@Data</span><br><span style="color: #c678dd;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">class</span> <span style="color: #e6c07b;line-height: 26px;">UserClass</span> </span>{<br> <span style="color: #c678dd;line-height: 26px;">private</span> String className;<br> <span style="color: #61aeee;line-height: 26px;">@Valid</span><br> <span style="color: #c678dd;line-height: 26px;">private</span> User user;<br>}<br><br><span style="color: #61aeee;line-height: 26px;">@PostMapping</span>(<span style="color: #98c379;line-height: 26px;">"checkBodyMultilevelParam"</span>)<br><span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span> String <span style="color: #61aeee;line-height: 26px;">checkBodyMultilevelParam</span><span style="line-height: 26px;">(@RequestBody @Valid UserClass userClass)</span></span>{<br> <span style="color: #c678dd;line-height: 26px;">return</span> <span style="color: #98c379;line-height: 26px;">"ok"</span>;<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;">当 age=2 时,校验不通过,提示年龄必须大于 10 岁</p> <p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.4397089397089397" data-s="300,640" src="/upload/31aa65bc41cb355ce158c1103bca80a4.png" data-type="png" data-w="1924" style=""></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"> image-20230727120123441 </figcaption> </figure> <h3 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 20px;margin: 0.6em auto;padding-left: 10px;border-left: 2px solid rgb(0, 150, 136);"><span style="display: none;"></span><span>集合参数校验</span><span style="display: none;"></span></h3> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;text-align: justify;">类上添加@Validated</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;text-align: justify;">在请求对象前面添加注解@valid,用@validate 没有效果</p> </section></li> </ul> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/nibxxlib1VaPfcUVibsaVCdpT5SQY72cdbDEpQtw1lOols8Dskz7WPOFr8GfeCBXgJCfeZxACuHXJegadicO6icib3ib7N5j1aMiaEv5/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="color: #61aeee;line-height: 26px;">@RestController</span><br><span style="color: #61aeee;line-height: 26px;">@RequestMapping</span>(<span style="color: #98c379;line-height: 26px;">"/paramTest"</span>)<br><span style="color: #61aeee;line-height: 26px;">@Validated</span><br><span style="color: #c678dd;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">class</span> <span style="color: #e6c07b;line-height: 26px;">ParamTestController</span> </span>{<br> <span style="color: #61aeee;line-height: 26px;">@PostMapping</span>(<span style="color: #98c379;line-height: 26px;">"checkList"</span>)<br> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span> String <span style="color: #61aeee;line-height: 26px;">checkList</span><span style="line-height: 26px;">(@RequestBody @Valid List<User> users)</span> </span>{<br> <span style="color: #c678dd;line-height: 26px;">return</span> <span style="color: #98c379;line-height: 26px;">"ok"</span>;<br> }<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;">但是如果要分组校验呢,只能用 validate,但是 validate 又没有效果,怎么办呢。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;"><strong>方法一:</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;">新建对象,将 list 当做属性</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;">缺点:这样修改的话,请求的参数结构就会改变.</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;"><strong>方法二:</strong></p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;text-align: justify;">实现 list</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;text-align: justify;">在 list 属性上添加 valid 注解</p> </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;">这样 ValidList 与 java.util.List 的对外功能完全一致,无需改变集合结构</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/nibxxlib1VaPfcUVibsaVCdpT5SQY72cdbDEpQtw1lOols8Dskz7WPOFr8GfeCBXgJCfeZxACuHXJegadicO6icib3ib7N5j1aMiaEv5/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="color: #61aeee;line-height: 26px;">@Data</span><br><span style="color: #c678dd;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">class</span> <span style="color: #e6c07b;line-height: 26px;">ValidList</span><<span style="color: #e6c07b;line-height: 26px;">E</span>> <span style="color: #c678dd;line-height: 26px;">implements</span> <span style="color: #e6c07b;line-height: 26px;">List</span><<span style="color: #e6c07b;line-height: 26px;">E</span>> </span>{<br><br> <span style="color: #61aeee;line-height: 26px;">@Valid</span><br> <span style="color: #c678dd;line-height: 26px;">private</span> List<E> list = <span style="color: #c678dd;line-height: 26px;">new</span> LinkedList<>();<br> <span style="color: #61aeee;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span> <span style="color: #c678dd;line-height: 26px;">int</span> <span style="color: #61aeee;line-height: 26px;">size</span><span style="line-height: 26px;">()</span> </span>{<br> <span style="color: #c678dd;line-height: 26px;">return</span> list.size();<br> }<br> <span style="color: #61aeee;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span> <span style="color: #c678dd;line-height: 26px;">boolean</span> <span style="color: #61aeee;line-height: 26px;">isEmpty</span><span style="line-height: 26px;">()</span> </span>{<br> <span style="color: #c678dd;line-height: 26px;">return</span> list.isEmpty();<br> }<br> <span style="color: #61aeee;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span> <span style="color: #c678dd;line-height: 26px;">boolean</span> <span style="color: #61aeee;line-height: 26px;">contains</span><span style="line-height: 26px;">(Object o)</span> </span>{<br> <span style="color: #c678dd;line-height: 26px;">return</span> list.contains(o);<br> }<br> <span style="color: #61aeee;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span> Iterator<E> <span style="color: #61aeee;line-height: 26px;">iterator</span><span style="line-height: 26px;">()</span> </span>{<br> <span style="color: #c678dd;line-height: 26px;">return</span> list.iterator();<br> }<br> <span style="color: #61aeee;line-height: 26px;">@Override</span><br> <span style="color: #c678dd;line-height: 26px;">public</span> Object[] toArray() {<br> <span style="color: #c678dd;line-height: 26px;">return</span> list.toArray();<br> }<br> <span style="color: #61aeee;line-height: 26px;">@Override</span><br> <span style="color: #c678dd;line-height: 26px;">public</span> <T> T[] toArray(T[] a) {<br> <span style="color: #c678dd;line-height: 26px;">return</span> list.toArray(a);<br> }<br> <span style="color: #61aeee;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span> <span style="color: #c678dd;line-height: 26px;">boolean</span> <span style="color: #61aeee;line-height: 26px;">add</span><span style="line-height: 26px;">(E e)</span> </span>{<br> <span style="color: #c678dd;line-height: 26px;">return</span> list.add(e);<br> }<br> <span style="color: #61aeee;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span> <span style="color: #c678dd;line-height: 26px;">boolean</span> <span style="color: #61aeee;line-height: 26px;">remove</span><span style="line-height: 26px;">(Object o)</span> </span>{<br> <span style="color: #c678dd;line-height: 26px;">return</span> list.remove(o);<br> }<br> <span style="color: #61aeee;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span> <span style="color: #c678dd;line-height: 26px;">boolean</span> <span style="color: #61aeee;line-height: 26px;">containsAll</span><span style="line-height: 26px;">(Collection<?> c)</span> </span>{<br> <span style="color: #c678dd;line-height: 26px;">return</span> list.containsAll(c);<br> }<br> <span style="color: #61aeee;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span> <span style="color: #c678dd;line-height: 26px;">boolean</span> <span style="color: #61aeee;line-height: 26px;">addAll</span><span style="line-height: 26px;">(Collection<? extends E> c)</span> </span>{<br> <span style="color: #c678dd;line-height: 26px;">return</span> list.addAll(c);<br> }<br> <span style="color: #61aeee;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span> <span style="color: #c678dd;line-height: 26px;">boolean</span> <span style="color: #61aeee;line-height: 26px;">addAll</span><span style="line-height: 26px;">(<span style="color: #c678dd;line-height: 26px;">int</span> index, Collection<? extends E> c)</span> </span>{<br> <span style="color: #c678dd;line-height: 26px;">return</span> list.addAll(index, c);<br> }<br> <span style="color: #61aeee;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span> <span style="color: #c678dd;line-height: 26px;">boolean</span> <span style="color: #61aeee;line-height: 26px;">removeAll</span><span style="line-height: 26px;">(Collection<?> c)</span> </span>{<br> <span style="color: #c678dd;line-height: 26px;">return</span> list.removeAll(c);<br> }<br> <span style="color: #61aeee;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span> <span style="color: #c678dd;line-height: 26px;">boolean</span> <span style="color: #61aeee;line-height: 26px;">retainAll</span><span style="line-height: 26px;">(Collection<?> c)</span> </span>{<br> <span style="color: #c678dd;line-height: 26px;">return</span> list.retainAll(c);<br> }<br> <span style="color: #61aeee;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span> <span style="color: #c678dd;line-height: 26px;">void</span> <span style="color: #61aeee;line-height: 26px;">clear</span><span style="line-height: 26px;">()</span> </span>{<br> list.clear();<br> }<br> <span style="color: #61aeee;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span> E <span style="color: #61aeee;line-height: 26px;">get</span><span style="line-height: 26px;">(<span style="color: #c678dd;line-height: 26px;">int</span> index)</span> </span>{<br> <span style="color: #c678dd;line-height: 26px;">return</span> list.get(index);<br> }<br> <span style="color: #61aeee;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span> E <span style="color: #61aeee;line-height: 26px;">set</span><span style="line-height: 26px;">(<span style="color: #c678dd;line-height: 26px;">int</span> index, E element)</span> </span>{<br> <span style="color: #c678dd;line-height: 26px;">return</span> list.set(index, element);<br> }<br> <span style="color: #61aeee;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span> <span style="color: #c678dd;line-height: 26px;">void</span> <span style="color: #61aeee;line-height: 26px;">add</span><span style="line-height: 26px;">(<span style="color: #c678dd;line-height: 26px;">int</span> index, E element)</span> </span>{<br> list.add(index, element);<br> }<br> <span style="color: #61aeee;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span> E <span style="color: #61aeee;line-height: 26px;">remove</span><span style="line-height: 26px;">(<span style="color: #c678dd;line-height: 26px;">int</span> index)</span> </span>{<br> <span style="color: #c678dd;line-height: 26px;">return</span> list.remove(index);<br> }<br> <span style="color: #61aeee;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span> <span style="color: #c678dd;line-height: 26px;">int</span> <span style="color: #61aeee;line-height: 26px;">indexOf</span><span style="line-height: 26px;">(Object o)</span> </span>{<br> <span style="color: #c678dd;line-height: 26px;">return</span> list.indexOf(o);<br> }<br> <span style="color: #61aeee;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span> <span style="color: #c678dd;line-height: 26px;">int</span> <span style="color: #61aeee;line-height: 26px;">lastIndexOf</span><span style="line-height: 26px;">(Object o)</span> </span>{<br> <span style="color: #c678dd;line-height: 26px;">return</span> list.lastIndexOf(o);<br> }<br> <span style="color: #61aeee;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span> ListIterator<E> <span style="color: #61aeee;line-height: 26px;">listIterator</span><span style="line-height: 26px;">()</span> </span>{<br> <span style="color: #c678dd;line-height: 26px;">return</span> list.listIterator();<br> }<br> <span style="color: #61aeee;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span> ListIterator<E> <span style="color: #61aeee;line-height: 26px;">listIterator</span><span style="line-height: 26px;">(<span style="color: #c678dd;line-height: 26px;">int</span> index)</span> </span>{<br> <span style="color: #c678dd;line-height: 26px;">return</span> list.listIterator(index);<br> }<br> <span style="color: #61aeee;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span> List<E> <span style="color: #61aeee;line-height: 26px;">subList</span><span style="line-height: 26px;">(<span style="color: #c678dd;line-height: 26px;">int</span> fromIndex, <span style="color: #c678dd;line-height: 26px;">int</span> toIndex)</span> </span>{<br> <span style="color: #c678dd;line-height: 26px;">return</span> list.subList(fromIndex, toIndex);<br> }<br>}<br><br> <span style="color: #61aeee;line-height: 26px;">@PostMapping</span>(<span style="color: #98c379;line-height: 26px;">"checkValidList"</span>)<br> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span> String <span style="color: #61aeee;line-height: 26px;">checkValidList</span><span style="line-height: 26px;">(@RequestBody @Valid ValidList<User> users)</span> </span>{<br> <span style="color: #c678dd;line-height: 26px;">return</span> <span style="color: #98c379;line-height: 26px;">"ok"</span>;<br> }<br></code></pre> <p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.47107438016528924" data-s="300,640" src="/upload/6d83d8c7b27d3708e0eb950c316cadbe.png" data-type="png" data-w="1936" style=""></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"> image-20230727161714930 </figcaption> </figure> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 22px;color: rgb(0, 150, 136);padding-left: 10px;margin: 1em auto;border-left: 3px solid rgb(0, 150, 136);"><span style="display: none;"></span><span>get 请求参数校验</span><span></span></h2> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;text-align: justify;">在类上使用@Validated 注解</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;text-align: justify;">在参数前面添加参数校验的注解</p> </section></li> </ul> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/nibxxlib1VaPfcUVibsaVCdpT5SQY72cdbDEpQtw1lOols8Dskz7WPOFr8GfeCBXgJCfeZxACuHXJegadicO6icib3ib7N5j1aMiaEv5/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="color: #61aeee;line-height: 26px;">@RestController</span><br><span style="color: #61aeee;line-height: 26px;">@RequestMapping</span>(<span style="color: #98c379;line-height: 26px;">"/paramTest"</span>)<br><span style="color: #61aeee;line-height: 26px;">@Validated</span><br><span style="color: #c678dd;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">class</span> <span style="color: #e6c07b;line-height: 26px;">ParamTestController</span> </span>{<br> <span style="color: #61aeee;line-height: 26px;">@GetMapping</span>(<span style="color: #98c379;line-height: 26px;">"checkParam"</span>)<br> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span> String <span style="color: #61aeee;line-height: 26px;">checkParam</span><span style="line-height: 26px;">(@RequestParam @Max(value = <span style="color: #d19a66;line-height: 26px;">99</span>, message = <span style="color: #98c379;line-height: 26px;">"不能大于99岁"</span>)</span> Integer age) </span>{<br> <span style="color: #c678dd;line-height: 26px;">return</span> <span style="color: #98c379;line-height: 26px;">"ok"</span>;<br> }<br><br> <span style="color: #61aeee;line-height: 26px;">@GetMapping</span>(<span style="color: #98c379;line-height: 26px;">"checkPath/{id}"</span>)<br> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span> String <span style="color: #61aeee;line-height: 26px;">checkPath</span><span style="line-height: 26px;">(@PathVariable @Pattern(regexp = <span style="color: #98c379;line-height: 26px;">"^[0-9]*$"</span>, message = <span style="color: #98c379;line-height: 26px;">"id参数值必须是正整数"</span>)</span> String id) </span>{<br> <span style="color: #c678dd;line-height: 26px;">return</span> <span style="color: #98c379;line-height: 26px;">"ok"</span>;<br> }<br>}<br></code></pre> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.45084745762711864" src="/upload/fbb67d429cb3e930359ca11310a7eab8.png" data-type="png" data-w="885" style="display: block;margin-right: auto;margin-left: auto;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"> image-20230727140607340 </figcaption> </figure> </section> <p style="margin-bottom: 0px;"><br></p> <p style="margin-bottom: 0px;"><br></p> <section data-mpa-template="t" mpa-from-tpl="t" style="margin-bottom: 0px;letter-spacing: 0.578px;white-space: normal;color: rgb(1, 1, 1);font-size: 16px;text-align: left;"> <section data-mpa-template="t" mpa-from-tpl="t" style="color: rgb(51, 51, 51);font-size: 17px;text-align: justify;"> <span style="letter-spacing: 0.544px;">我是臻大虾,</span> <span style="color: rgb(255, 104, 39);"><span style="letter-spacing: 0.544px;">分享</span></span> <span style="letter-spacing: 0.544px;">、</span> <span style="color: rgb(0, 209, 0);"><span style="letter-spacing: 0.544px;">收藏</span></span> <span style="letter-spacing: 0.544px;">、</span> <span style="color: rgb(0, 82, 255);"><span style="letter-spacing: 0.544px;">点赞</span></span> <span style="letter-spacing: 0.544px;">、</span> <span style="color: rgb(255, 169, 0);"><span style="letter-spacing: 0.544px;">在看</span></span> </section> </section> <p style="margin-bottom: 0px;letter-spacing: 0.578px;white-space: normal;color: rgb(1, 1, 1);font-size: 16px;text-align: left;"><span style="letter-spacing: 0.544px;color: rgb(51, 51, 51);font-size: 17px;text-align: justify;">你的支持是对我不断创作的极大鼓励,咱们下期见。</span></p> <p style="margin-bottom: 0px;letter-spacing: 0.578px;white-space: normal;color: rgb(1, 1, 1);font-size: 16px;text-align: left;"><span style="letter-spacing: 0.544px;color: rgb(51, 51, 51);font-size: 17px;text-align: justify;"><br></span></p> <section mpa-from-tpl="t" style="letter-spacing: 0.578px;white-space: normal;margin-bottom: 0px;"> <section data-mpa-template="t" mpa-from-tpl="t"> <section data-mpa-template="t" mpa-from-tpl="t"> <section data-mid="" mpa-from-tpl="t" style="padding-right: 34px;display: flex;align-items: center;width: 578px;justify-content: flex-end;"> <section data-mid="" mpa-from-tpl="t" style="display: flex;justify-content: flex-start;align-items: center;flex-direction: column;"> <section data-mid="" mpa-from-tpl="t" style="height: 36px;"> <p data-mid="" style="padding-left: 26px;font-size: 16px;font-weight: bold;color: rgb(120, 196, 220);line-height: 22px;border-bottom: 1px solid rgb(120, 196, 220);">点个<span data-mid="" style="padding-right: 4px;padding-left: 4px;">在看</span>你最好看</p> </section> <section data-mid="" mpa-from-tpl="t" style="margin-top: -19px;margin-right: -19px;width: 20px;height: 6px;align-self: flex-end;"> <img data-ratio="0.3" src="/upload/d9be09d95420e4e38d4f60266e64862d.png" data-w="40" style="display: block;"> </section> </section> </section> </section> </section> <p><br mpa-from-tpl="t"></p> </section> <p style="letter-spacing: 0.578px;white-space: normal;margin-bottom: 0px;"><br></p> <p style="margin-bottom: 0px;letter-spacing: 0.578px;white-space: normal;color: rgb(1, 1, 1);font-size: 16px;text-align: left;"><span style="letter-spacing: 0.544px;color: rgb(51, 51, 51);font-size: 17px;text-align: justify;"></span></p> <p style="margin-bottom: 0px;"><br></p> <p><br></p> <p style="display: none;"> <mp-style-type data-value="3"></mp-style-type></p>
作者:微信小助手
<p style="text-align: center;margin-bottom: 0px;"><span style="color: rgb(115, 250, 121);">点击上方蓝字 </span><img class="rich_pages wxw-img" data-ratio="1" src="/upload/d9a2d18d22a45bf3cb8fabf91a20a184.png" data-type="png" data-w="64" style="display:inline-block;width:20px;vertical-align:text-bottom;"><span style="color: rgb(115, 250, 121);"> 关注大侠之运维</span></p> <blockquote data-tool="mdnice编辑器" data-style="border-top: none; border-right: none; border-bottom: none; font-size: 0.9em; overflow: auto; background: rgb(251, 249, 253); color: rgb(106, 115, 125); margin-bottom: 20px; margin-top: 20px; padding: 15px 20px; line-height: 27px; border-left-color: rgb(53, 179, 120);" class="js_darkmode__1" style="margin-top: 20px;margin-bottom: 20px;padding: 15px 20px;border-left-color: rgb(53, 179, 120);color: rgb(106, 115, 125);font-size: 0.9em;line-height: 27px;text-wrap: wrap;outline: 0px;border-top: none;border-right: none;border-bottom: none;background: rgb(251, 249, 253);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.544px;text-align: left;overflow: auto;visibility: visible;"> <p data-style="line-height: 26px; font-size: 15px; color: rgb(89, 89, 89);" class="js_darkmode__2" style="outline: 0px;color: rgb(89, 89, 89);line-height: 26px;font-size: 15px;visibility: visible;">大家好,这里是大侠之运维,每天分享各类干货。</p> </blockquote> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><span style="letter-spacing: 0px;">用过cat、pinpoint、skywalking等链路监控系统,各有优劣,但用的最多的还是pinpoint,工作6年,其中有4年都在用pinpoint,所以也比较熟悉,之前也有过介绍如何安装部署,可以去参考如下文章:</span><br></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.5646630236794171" src="/upload/85270de0d9be11df328371a1c4b6cec8.png" data-type="png" data-w="1647" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <h1 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 24px;"><span style="display: none;"></span>深入分布式应用性能监控:Pinpoint</h1> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在现代互联网时代,分布式应用已经成为主流。这些应用通常由多个组件组成,它们相互协作以提供各种服务。然而,这种复杂性也带来了性能监控的挑战。开发人员和运维团队需要深入了解分布式系统中的事务流程,以确保高性能、高可用性和卓越的用户体验。而这正是Pinpoint所擅长的领域。本文将深入介绍Pinpoint,这一强大的分布式应用性能监控工具,帮助您了解其原理、功能和如何使用它来提升应用程序的性能。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>1. 什么是Pinpoint?</h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Pinpoint是一个开源的应用性能监控工具,专为分布式应用程序而设计。它的目标是帮助开发人员和运维团队深入了解分布式系统中的事务流程和性能表现。Pinpoint跟踪并可视化事务的执行过程,以便用户能够识别问题并改进应用程序性能。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>2. 为什么需要Pinpoint?</h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在分布式系统中,一个应用通常由多个不同的组件组成,这些组件之间相互通信,有时还需要与外部服务进行API调用。如何监控这些组件之间的交互以及每个组件的性能变得非常关键。而Pinpoint的出现弥补了这一监控领域的不足,为开发人员和运维人员提供了一个强大的工具,帮助他们理解和改进分布式应用的性能。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>3. Pinpoint的主要特性</h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Pinpoint具有许多强大的特性,以下是一些主要功能:</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>3.1 服务器地图(ServerMap)<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Pinpoint通过ServerMap可视化展示组件之间的互联关系,帮助用户理解分布式系统的拓扑结构。用户可以单击节点以查看有关组件的详细信息,如当前状态和事务计数。这有助于快速识别问题所在。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>3.2 实时活跃线程图表(Realtime Active Thread Chart)<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Pinpoint能够实时监控应用程序内部的活跃线程。这有助于用户了解应用程序的并发情况,识别潜在的性能瓶颈。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>3.3 请求/响应分布图表(Request/Response Scatter Chart)<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Pinpoint可以可视化展示请求计数和响应模式的变化趋势。这有助于识别潜在的问题,并提供了更详细的信息以进行进一步的分析。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>3.4 调用堆栈(CallStack)<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Pinpoint提供了代码级别的可视化,允许用户深入了解分布式环境中的每个事务。这有助于识别瓶颈和故障点,从而加速故障排除过程。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.5538740920096852" src="/upload/b275f15f091b0d0f41ad0697075073a0.png" data-type="png" data-w="1652" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>3.5 检查器(Inspector)<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Pinpoint的Inspector功能允许用户查看有关应用程序的附加信息,如CPU使用率、内存和垃圾收集情况、每秒事务数(TPS)以及JVM参数。这些信息对于性能优化和故障排除非常有帮助。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.5005347593582887" src="/upload/ee2031550ed6966129978a57ea009dd7.png" data-type="png" data-w="1870" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>4. 如何使用Pinpoint?</h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">使用Pinpoint来监控分布式应用程序的性能是一个多步骤的过程。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.5179806362378977" src="/upload/61602582476a1c475f10ec746e06b275.png" data-type="png" data-w="1446" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>4.1 安装Pinpoint代理<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">首先,您需要在应用程序的各个组件上安装Pinpoint代理。这些代理将负责收集性能数据并将其发送到Pinpoint服务器。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>4.2 配置Pinpoint服务器<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">您需要设置Pinpoint服务器以接收和存储从代理发送的性能数据。Pinpoint服务器将数据可视化并提供给用户。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>4.3 监控和分析<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">一旦Pinpoint代理和服务器都设置好了,您就可以开始监控分布式应用程序的性能。使用Pinpoint的各种功能来跟踪事务、识别性能问题并进行进一步的分析。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.5179806362378977" src="/upload/413c4b738dfe9e5487c48b0c12ad324e.png" data-type="png" data-w="1446" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>4.4 优化性能<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">根据Pinpoint提供的数据和分析结果,您可以采取必要的措施来优化应用程序的性能。这可能包括代码优化、资源调整或架构改进。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>5. Pinpoint的优势</h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Pinpoint作为一款开源工具,具有以下优势:</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>5.1 高度可定制性<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Pinpoint是开源的,因此用户可以根据自己的需求进行定制。您可以创建自定义插件、规则和仪表板,以满足特定的监控需求。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>5.2 社区支持<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Pinpoint拥有活跃的开发和用户社区,用户可以在社区中获取支持、分享经验并获取更新和扩展。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>5.3 商业友好的开源许可<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Pinpoint采用商业友好的开源许可,使其非常适合用于商业环境中。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>6. 快速安装</h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">看了下最新的安装步骤,竟然有了windwos的安装部署,可以快速安装试用</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">可以访问如下路径:</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><strong>https://pinpoint-apm.gitbook.io/pinpoint/getting-started/quickstart/quickstart.win.en</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">github可以访问的直接到如下链接去下载就可以,目前支持windows</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><strong>https://github.com/1Remote/1Remote/releases</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">github如果无法访问的话,可以后台直接私信</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>7. 总结</h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Pinpoint是一款强大的分布式应用性能监控工具,旨在帮助开发人员和运维团队深入了解分布式系统中的事务流程和性能表现。它提供了一系列功能,包括服务器地图、实时活跃线程图表、请求/响应分布图表、调用堆栈和检查器,使用户能够识别问题并优化应用程序性能。作为一款开源工具,Pinpoint具有高度可定制性、社区支持和商业友好的开源许可,适用于各种分布式应用监控场景。无论是开发人员还是运维团队,Pinpoint都是一个强大的工具,可帮助他们提升分布式应用的性能和可用性,提供卓越的用户体验。</p> </section> <p style="margin-bottom: 0px;letter-spacing: 0.578px;text-indent: 0em;text-wrap: wrap;text-align: left;line-height: 1.75em;"><span style="color: rgb(0, 0, 0);font-size: 18px;"></span></p> <blockquote data-tool="mdnice编辑器" data-style="border-top: none; border-right: none; border-bottom: none; font-size: 0.9em; overflow: auto; background: rgb(251, 249, 253); color: rgb(106, 115, 125); margin-bottom: 20px; margin-top: 20px; padding: 15px 20px; line-height: 27px; border-left-color: rgb(53, 179, 120);" class="js_darkmode__35" style="margin-top: 20px;margin-bottom: 20px;padding: 15px 20px;outline: 0px;border-left-color: rgb(53, 179, 120);color: rgb(106, 115, 125);font-size: 0.9em;border-top: none;border-right: none;border-bottom: none;background: rgb(251, 249, 253);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.544px;text-align: left;text-wrap: wrap;overflow: auto;line-height: 27px;"> <p data-style="line-height: 26px; font-size: 15px; color: rgb(89, 89, 89);" class="js_darkmode__36" style="outline: 0px;color: rgb(89, 89, 89);line-height: 26px;font-size: 15px;">大侠之运维,相识便是缘<br style="outline: 0px;">收集不易,点赞、留言、分享就是大侠🦸♀️写下去的动力!</p> </blockquote> <p style="text-align: left;margin-bottom: 0px;"><span style="color: rgb(0, 0, 0);"><br></span></p> <section class="mp_profile_iframe_wrp"> <mp-common-profile class="js_uneditable custom_select_card mp_profile_iframe" data-id="MzU4MjY3Mzc3OQ==" data-pluginname="mpprofile" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/ujHTnqg0DiajOpk1naM3ibsMyNxyRvGjKVgsBMdGgYjgmNuiajtKicVnI0tdrdKDW8IpGACcOJsNPupQNWAMicpfickg/0?wx_fmt=png" data-nickname="大侠之运维" data-alias="" data-signature="分享关于运维的资料、工作经验、日常感悟。" data-from="0" data-is_biz_ban="0"></mp-common-profile> </section> <p style="text-align: center;margin-bottom: 0px;"><span style="font-size: 16px;"><strong>👆点击查看更多内容👆</strong></span></p> <p style="text-align: center;margin-bottom: 0px;"><span style="font-size: 16px;"><strong><br></strong></span></p> <p style="text-align: left;margin-bottom: 0px;"><span style="font-size: 16px;"><strong>推荐阅读 ⬇️ 都是高赞<br></strong></span></p> <p style="text-align: left;margin-bottom: 0px;"><span style="font-size: 12px;"><strong><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU4MjY3Mzc3OQ==&mid=2247485614&idx=1&sn=93411899429a548ecd9f378f00722db7&chksm=fdb5fcb2cac275a4318bd730f017b59c3afedf2964ebae026b2a8d4ffed81cf6bbf4292bbfbf&scene=21#wechat_redirect" textvalue="Linux超级漂亮的Shell" linktype="text" imgurl="" imgdata="null" data-itemshowtype="0" tab="innerlink" data-linktype="2">Linux超级漂亮的Shell</a><br></strong></span></p> <p style="text-align: left;margin-bottom: 0px;"><span style="font-size: 12px;"><strong><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU4MjY3Mzc3OQ==&mid=2247485647&idx=1&sn=9f1fffd1f406fbfebd3be499114c8943&chksm=fdb5fcd3cac275c5fc560d752a16d09d68cb9c479a7dd863e366ab180c3f3c5ca224e2a8fbd8&scene=21#wechat_redirect" textvalue="notepad++不用了,我用notepad next" linktype="text" imgurl="" imgdata="null" data-itemshowtype="0" tab="innerlink" data-linktype="2">notepad++不用了,我用notepad next</a></strong></span></p> <section style="text-align: left;line-height: 1.75em;margin-bottom: 0px;"> <a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU4MjY3Mzc3OQ==&mid=2247485708&idx=1&sn=ccbf3d0ce863cf65d0a1d309bf8370d5&chksm=fdb5fd10cac274068327f2a841aed675bdc0131dcc02a5e1d2acf2f3e7e922618375098d3674&scene=21#wechat_redirect" textvalue="神器,代码画架构图,部署图,yyds" linktype="text" imgurl="" imgdata="null" data-itemshowtype="0" tab="innerlink" data-linktype="2"><span style="font-size: 12px;"><strong>再见了 Xshell、iTerm2、FinalShell,mobaxterm,这款开源的终端工具真香</strong></span></a> </section> <section style="text-align: left;line-height: 1.75em;margin-bottom: 0px;"> <a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU4MjY3Mzc3OQ==&mid=2247486699&idx=1&sn=e437eaead5d6db10fd1dfad5da5189a1&chksm=fdb5f8f7cac271e111819744fb554a759e5fdd404b32878b161b41f524fe88344e3f170c3434&scene=21#wechat_redirect" textvalue="1.5kstar,实用开源工具推荐" linktype="text" imgurl="" imgdata="null" data-itemshowtype="0" tab="innerlink" data-linktype="2"><strong style="font-size: 12px;letter-spacing: 0.578px;text-align: left;text-wrap: wrap;">windows远程不要再用mstsc了,推荐一款神器</strong></a> </section> <p style="text-align: center;margin-bottom: 0em;"> </p> <p style="text-align: left;margin-bottom: 0px;"><span style="outline: 0px;letter-spacing: 0.544px;text-align: left;text-wrap: wrap;widows: 1;caret-color: rgb(60, 60, 60);font-size: 15px;color: rgba(0, 0, 0, 0.8);font-family: Optima-Regular, PingFangTC-light;">PS:各类干货,每天更新,防止错过,记得读完点一下</span><strong style="outline: 0px;letter-spacing: 0.544px;text-align: left;text-wrap: wrap;widows: 1;caret-color: rgb(60, 60, 60);color: rgba(0, 0, 0, 0.8);font-family: Optima-Regular, PingFangTC-light;font-size: 12px;">“</strong><span style="outline: 0px;color: rgb(34, 34, 34);font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;text-align: left;text-wrap: wrap;font-size: 15px;"><strong style="outline: 0px;letter-spacing: 0.544px;widows: 1;caret-color: rgb(60, 60, 60);color: rgba(0, 0, 0, 0.8);font-family: Optima-Regular, PingFangTC-light;font-size: 12px;">在看</strong></span><strong style="outline: 0px;letter-spacing: 0.544px;text-align: left;text-wrap: wrap;widows: 1;caret-color: rgb(60, 60, 60);color: rgba(0, 0, 0, 0.8);font-family: Optima-Regular, PingFangTC-light;font-size: 12px;">”</strong><span style="outline: 0px;letter-spacing: 0.544px;text-align: left;text-wrap: wrap;widows: 1;caret-color: rgb(60, 60, 60);font-size: 15px;color: rgba(0, 0, 0, 0.8);font-family: Optima-Regular, PingFangTC-light;">,加个</span><strong style="outline: 0px;letter-spacing: 0.544px;text-align: left;text-wrap: wrap;widows: 1;caret-color: rgb(60, 60, 60);color: rgba(0, 0, 0, 0.8);font-family: Optima-Regular, PingFangTC-light;font-size: 12px;">“</strong><span style="outline: 0px;color: rgb(34, 34, 34);font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;text-align: left;text-wrap: wrap;font-size: 15px;"><strong style="outline: 0px;letter-spacing: 0.544px;widows: 1;caret-color: rgb(60, 60, 60);color: rgba(0, 0, 0, 0.8);font-family: Optima-Regular, PingFangTC-light;font-size: 12px;">星标</strong></span><strong style="outline: 0px;letter-spacing: 0.544px;text-align: left;text-wrap: wrap;widows: 1;caret-color: rgb(60, 60, 60);color: rgba(0, 0, 0, 0.8);font-family: Optima-Regular, PingFangTC-light;font-size: 12px;">”</strong><span style="outline: 0px;letter-spacing: 0.544px;text-align: left;text-wrap: wrap;widows: 1;caret-color: rgb(60, 60, 60);font-size: 15px;color: rgba(0, 0, 0, 0.8);font-family: Optima-Regular, PingFangTC-light;">,这样每次新文章推送才会第一时间出现在你的订阅列表里。</span><span style="outline: 0px;letter-spacing: 0.544px;text-align: left;text-wrap: wrap;widows: 1;caret-color: rgb(60, 60, 60);font-size: 15px;font-family: Optima-Regular, PingFangTC-light;visibility: visible;color: rgb(255, 0, 0);">点<strong style="outline: 0px;">“</strong><strong style="outline: 0px;">在看</strong><strong style="outline: 0px;">”</strong>支持下吧!</span></p> <p><br></p> <p style="display: none;"> <mp-style-type data-value="3"></mp-style-type></p>