文章列表

Linux bash

作者:じ☆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 ```

Linux bash 进阶

作者:じ☆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`}" # 将会输出默

Spring 中经典的 9 种设计模式,打死也要记住啊!

作者:微信小助手

<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="outline: 0px;font-family: system-ui, -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, 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>&nbsp;,比如 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>&nbsp;,实现了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>&nbsp;,实现了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>&nbsp;,实现了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;">&nbsp;可以将原来硬编码的依赖,通过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>&nbsp;通过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: &quot;Operator Mono&quot;, 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: &quot;Operator Mono&quot;, 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>&nbsp;Object&nbsp;<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&nbsp;beanName)</span></span>{<br style="outline: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="outline: 0px;color: rgb(117, 113, 94);line-height: 26px;">//参数true设置标识允许早期依赖</span><br style="outline: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="outline: 0px;color: rgb(249, 38, 114);font-weight: bold;line-height: 26px;">return</span>&nbsp;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>&nbsp;Object&nbsp;<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&nbsp;beanName,&nbsp;<span style="outline: 0px;color: rgb(249, 38, 114);font-weight: bold;line-height: 26px;">boolean</span>&nbsp;allowEarlyReference)</span>&nbsp;</span>{<br style="outline: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="outline: 0px;color: rgb(117, 113, 94);line-height: 26px;">//检查缓存中是否存在实例</span><br style="outline: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;Object&nbsp;singletonObject&nbsp;=&nbsp;<span style="outline: 0px;color: rgb(249, 38, 114);font-weight: bold;line-height: 26px;">this</span>.singletonObjects.get(beanName);<br style="outline: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;<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, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, 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, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, 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, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, 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, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, 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: &quot;Operator Mono&quot;, 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, &quot;Andale Mono&quot;, &quot;Ubuntu Mono&quot;, 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, &quot;Andale Mono&quot;, &quot;Ubuntu Mono&quot;, 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, &quot;Andale Mono&quot;, &quot;Ubuntu Mono&quot;, 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, &quot;Andale Mono&quot;, &quot;Ubuntu Mono&quot;, 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, &quot;Andale Mono&quot;, &quot;Ubuntu Mono&quot;, 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, &quot;Andale Mono&quot;, &quot;Ubuntu Mono&quot;, 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;">&lt;</span>DetectorType<span style="color: #00e0e0;">&gt;</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;">=&gt;</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, &quot;Andale Mono&quot;, &quot;Ubuntu Mono&quot;, 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, &quot;Andale Mono&quot;, &quot;Ubuntu Mono&quot;, 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: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;max-width: unset;">detectors</code> 定义:</p> <pre data-line="75" style="color: rgb(248, 248, 242);font-family: Consolas, Monaco, &quot;Andale Mono&quot;, &quot;Ubuntu Mono&quot;, 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, &quot;Andale Mono&quot;, &quot;Ubuntu Mono&quot;, 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: &quot;Operator Mono&quot;, 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: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;max-width: unset;">ondevtoolopen</code> 里执行业务逻辑,比如做数据上报、用户行为分析等:</p> <pre data-line="84" style="color: rgb(248, 248, 242);font-family: Consolas, Monaco, &quot;Andale Mono&quot;, &quot;Ubuntu Mono&quot;, 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, &quot;Andale Mono&quot;, &quot;Ubuntu Mono&quot;, 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: &quot;Operator Mono&quot;, 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: &quot;Operator Mono&quot;, 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, &quot;Andale Mono&quot;, &quot;Ubuntu Mono&quot;, 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, &quot;Andale Mono&quot;, &quot;Ubuntu Mono&quot;, 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>

基于 Axios 封装一个完美的双 token 无感刷新

作者:微信小助手

<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, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, 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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/WAibKjHvK5nF8zePiaMqZPibiaJFLz2woOlia38s38Kiaic4MMvbjZk3sL0SicJEhdbsTPFM317YQAJVrDMAqXXl7B4CYhTKXUjnMeng/640?wx_fmt=svg&quot;) 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;">&nbsp;npx&nbsp;nest&nbsp;new&nbsp;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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/WAibKjHvK5nF8zePiaMqZPibiaJFLz2woOlia38s38Kiaic4MMvbjZk3sL0SicJEhdbsTPFM317YQAJVrDMAqXXl7B4CYhTKXUjnMeng/640?wx_fmt=svg&quot;) 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&nbsp;run&nbsp;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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/WAibKjHvK5nF8zePiaMqZPibiaJFLz2woOlia38s38Kiaic4MMvbjZk3sL0SicJEhdbsTPFM317YQAJVrDMAqXXl7B4CYhTKXUjnMeng/640?wx_fmt=svg&quot;) 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()&nbsp;userDto:&nbsp;UserDto)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #e6c07b;line-height: 26px;">console</span>.log(userDto);<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;<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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/WAibKjHvK5nF8zePiaMqZPibiaJFLz2woOlia38s38Kiaic4MMvbjZk3sL0SicJEhdbsTPFM317YQAJVrDMAqXXl7B4CYhTKXUjnMeng/640?wx_fmt=svg&quot;) 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>&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">class</span>&nbsp;<span style="color: #e6c07b;line-height: 26px;">UserDto</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #d19a66;line-height: 26px;">username</span>:&nbsp;string;<br>&nbsp;&nbsp;&nbsp;&nbsp;password:&nbsp;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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/WAibKjHvK5nF8zePiaMqZPibiaJFLz2woOlia38s38Kiaic4MMvbjZk3sL0SicJEhdbsTPFM317YQAJVrDMAqXXl7B4CYhTKXUjnMeng/640?wx_fmt=svg&quot;) 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>&nbsp;users&nbsp;=&nbsp;[<br>&nbsp;&nbsp;{&nbsp;<span style="color: #d19a66;line-height: 26px;">username</span>:&nbsp;<span style="color: #98c379;line-height: 26px;">'guang'</span>,&nbsp;<span style="color: #d19a66;line-height: 26px;">password</span>:&nbsp;<span style="color: #98c379;line-height: 26px;">'111111'</span>,&nbsp;<span style="color: #d19a66;line-height: 26px;">email</span>:&nbsp;<span style="color: #98c379;line-height: 26px;">'xxx@xxx.com'</span>},<br>&nbsp;&nbsp;{&nbsp;<span style="color: #d19a66;line-height: 26px;">username</span>:&nbsp;<span style="color: #98c379;line-height: 26px;">'dong'</span>,&nbsp;<span style="color: #d19a66;line-height: 26px;">password</span>:&nbsp;<span style="color: #98c379;line-height: 26px;">'222222'</span>,&nbsp;<span style="color: #d19a66;line-height: 26px;">email</span>:&nbsp;<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()&nbsp;userDto:&nbsp;UserDto)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">const</span>&nbsp;user&nbsp;=&nbsp;users.find(<span style="line-height: 26px;"><span style="line-height: 26px;">item</span>&nbsp;=&gt;</span>&nbsp;item.username&nbsp;===&nbsp;userDto.username);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">if</span>(!user)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">throw</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">new</span>&nbsp;BadRequestException(<span style="color: #98c379;line-height: 26px;">'用户不存在'</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">if</span>(user.password&nbsp;!==&nbsp;userDto.password)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">throw</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">new</span>&nbsp;BadRequestException(<span style="color: #98c379;line-height: 26px;">"密码错误"</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #d19a66;line-height: 26px;">userInfo</span>:&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #d19a66;line-height: 26px;">username</span>:&nbsp;user.username,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #d19a66;line-height: 26px;">email</span>:&nbsp;user.email<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;},<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #d19a66;line-height: 26px;">accessToken</span>:&nbsp;<span style="color: #98c379;line-height: 26px;">'xxx'</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #d19a66;line-height: 26px;">refreshToken</span>:&nbsp;<span style="color: #98c379;line-height: 26px;">'yyy'</span><br>&nbsp;&nbsp;&nbsp;&nbsp;};<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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/WAibKjHvK5nF8zePiaMqZPibiaJFLz2woOlia38s38Kiaic4MMvbjZk3sL0SicJEhdbsTPFM317YQAJVrDMAqXXl7B4CYhTKXUjnMeng/640?wx_fmt=svg&quot;) 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&nbsp;install&nbsp;@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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/WAibKjHvK5nF8zePiaMqZPibiaJFLz2woOlia38s38Kiaic4MMvbjZk3sL0SicJEhdbsTPFM317YQAJVrDMAqXXl7B4CYhTKXUjnMeng/640?wx_fmt=svg&quot;) 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

为什么 MyBatis 源码中,没有我那种 if···else

作者:微信小助手

<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, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, 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>

别再乱用了,这才是 @Validated 和 @Valid 的真正区别和用法!

作者:微信小助手

<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, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, 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: &quot;Operator Mono&quot;, 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: &quot;Operator Mono&quot;, 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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/nibxxlib1VaPfcUVibsaVCdpT5SQY72cdbDEpQtw1lOols8Dskz7WPOFr8GfeCBXgJCfeZxACuHXJegadicO6icib3ib7N5j1aMiaEv5/640?wx_fmt=svg&quot;) 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>&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">class</span>&nbsp;<span style="color: #e6c07b;line-height: 26px;">User</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@Min</span>(value&nbsp;=&nbsp;<span style="color: #d19a66;line-height: 26px;">10</span>,message&nbsp;=&nbsp;<span style="color: #98c379;line-height: 26px;">"年龄必须大于10岁"</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">private</span>&nbsp;Integer&nbsp;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>&nbsp;String&nbsp;<span style="color: #61aeee;line-height: 26px;">checkBodyParam</span><span style="line-height: 26px;">(@RequestBody&nbsp;@Valid&nbsp;User&nbsp;user)</span></span>{<br>&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;<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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/nibxxlib1VaPfcUVibsaVCdpT5SQY72cdbDEpQtw1lOols8Dskz7WPOFr8GfeCBXgJCfeZxACuHXJegadicO6icib3ib7N5j1aMiaEv5/640?wx_fmt=svg&quot;) 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>&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">class</span>&nbsp;<span style="color: #e6c07b;line-height: 26px;">UserClass</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">private</span>&nbsp;String&nbsp;className;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@Valid</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">private</span>&nbsp;User&nbsp;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>&nbsp;String&nbsp;<span style="color: #61aeee;line-height: 26px;">checkBodyMultilevelParam</span><span style="line-height: 26px;">(@RequestBody&nbsp;@Valid&nbsp;UserClass&nbsp;userClass)</span></span>{<br>&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;<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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/nibxxlib1VaPfcUVibsaVCdpT5SQY72cdbDEpQtw1lOols8Dskz7WPOFr8GfeCBXgJCfeZxACuHXJegadicO6icib3ib7N5j1aMiaEv5/640?wx_fmt=svg&quot;) 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>&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">class</span>&nbsp;<span style="color: #e6c07b;line-height: 26px;">ParamTestController</span>&nbsp;</span>{<br>&nbsp;<span style="color: #61aeee;line-height: 26px;">@PostMapping</span>(<span style="color: #98c379;line-height: 26px;">"checkList"</span>)<br>&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;String&nbsp;<span style="color: #61aeee;line-height: 26px;">checkList</span><span style="line-height: 26px;">(@RequestBody&nbsp;@Valid&nbsp;List&lt;User&gt;&nbsp;users)</span>&nbsp;</span>{<br>&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;<span style="color: #98c379;line-height: 26px;">"ok"</span>;<br>&nbsp;}<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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/nibxxlib1VaPfcUVibsaVCdpT5SQY72cdbDEpQtw1lOols8Dskz7WPOFr8GfeCBXgJCfeZxACuHXJegadicO6icib3ib7N5j1aMiaEv5/640?wx_fmt=svg&quot;) 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>&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">class</span>&nbsp;<span style="color: #e6c07b;line-height: 26px;">ValidList</span>&lt;<span style="color: #e6c07b;line-height: 26px;">E</span>&gt;&nbsp;<span style="color: #c678dd;line-height: 26px;">implements</span>&nbsp;<span style="color: #e6c07b;line-height: 26px;">List</span>&lt;<span style="color: #e6c07b;line-height: 26px;">E</span>&gt;&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@Valid</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">private</span>&nbsp;List&lt;E&gt;&nbsp;list&nbsp;=&nbsp;<span style="color: #c678dd;line-height: 26px;">new</span>&nbsp;LinkedList&lt;&gt;();<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">int</span>&nbsp;<span style="color: #61aeee;line-height: 26px;">size</span><span style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;list.size();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">boolean</span>&nbsp;<span style="color: #61aeee;line-height: 26px;">isEmpty</span><span style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;list.isEmpty();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">boolean</span>&nbsp;<span style="color: #61aeee;line-height: 26px;">contains</span><span style="line-height: 26px;">(Object&nbsp;o)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;list.contains(o);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;Iterator&lt;E&gt;&nbsp;<span style="color: #61aeee;line-height: 26px;">iterator</span><span style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;list.iterator();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;Object[]&nbsp;toArray()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;list.toArray();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;&lt;T&gt;&nbsp;T[]&nbsp;toArray(T[]&nbsp;a)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;list.toArray(a);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">boolean</span>&nbsp;<span style="color: #61aeee;line-height: 26px;">add</span><span style="line-height: 26px;">(E&nbsp;e)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;list.add(e);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">boolean</span>&nbsp;<span style="color: #61aeee;line-height: 26px;">remove</span><span style="line-height: 26px;">(Object&nbsp;o)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;list.remove(o);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">boolean</span>&nbsp;<span style="color: #61aeee;line-height: 26px;">containsAll</span><span style="line-height: 26px;">(Collection&lt;?&gt;&nbsp;c)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;list.containsAll(c);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">boolean</span>&nbsp;<span style="color: #61aeee;line-height: 26px;">addAll</span><span style="line-height: 26px;">(Collection&lt;?&nbsp;extends&nbsp;E&gt;&nbsp;c)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;list.addAll(c);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">boolean</span>&nbsp;<span style="color: #61aeee;line-height: 26px;">addAll</span><span style="line-height: 26px;">(<span style="color: #c678dd;line-height: 26px;">int</span>&nbsp;index,&nbsp;Collection&lt;?&nbsp;extends&nbsp;E&gt;&nbsp;c)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;list.addAll(index,&nbsp;c);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">boolean</span>&nbsp;<span style="color: #61aeee;line-height: 26px;">removeAll</span><span style="line-height: 26px;">(Collection&lt;?&gt;&nbsp;c)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;list.removeAll(c);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">boolean</span>&nbsp;<span style="color: #61aeee;line-height: 26px;">retainAll</span><span style="line-height: 26px;">(Collection&lt;?&gt;&nbsp;c)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;list.retainAll(c);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">void</span>&nbsp;<span style="color: #61aeee;line-height: 26px;">clear</span><span style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list.clear();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;E&nbsp;<span style="color: #61aeee;line-height: 26px;">get</span><span style="line-height: 26px;">(<span style="color: #c678dd;line-height: 26px;">int</span>&nbsp;index)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;list.get(index);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;E&nbsp;<span style="color: #61aeee;line-height: 26px;">set</span><span style="line-height: 26px;">(<span style="color: #c678dd;line-height: 26px;">int</span>&nbsp;index,&nbsp;E&nbsp;element)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;list.set(index,&nbsp;element);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">void</span>&nbsp;<span style="color: #61aeee;line-height: 26px;">add</span><span style="line-height: 26px;">(<span style="color: #c678dd;line-height: 26px;">int</span>&nbsp;index,&nbsp;E&nbsp;element)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list.add(index,&nbsp;element);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;E&nbsp;<span style="color: #61aeee;line-height: 26px;">remove</span><span style="line-height: 26px;">(<span style="color: #c678dd;line-height: 26px;">int</span>&nbsp;index)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;list.remove(index);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">int</span>&nbsp;<span style="color: #61aeee;line-height: 26px;">indexOf</span><span style="line-height: 26px;">(Object&nbsp;o)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;list.indexOf(o);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">int</span>&nbsp;<span style="color: #61aeee;line-height: 26px;">lastIndexOf</span><span style="line-height: 26px;">(Object&nbsp;o)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;list.lastIndexOf(o);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;ListIterator&lt;E&gt;&nbsp;<span style="color: #61aeee;line-height: 26px;">listIterator</span><span style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;list.listIterator();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;ListIterator&lt;E&gt;&nbsp;<span style="color: #61aeee;line-height: 26px;">listIterator</span><span style="line-height: 26px;">(<span style="color: #c678dd;line-height: 26px;">int</span>&nbsp;index)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;list.listIterator(index);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;List&lt;E&gt;&nbsp;<span style="color: #61aeee;line-height: 26px;">subList</span><span style="line-height: 26px;">(<span style="color: #c678dd;line-height: 26px;">int</span>&nbsp;fromIndex,&nbsp;<span style="color: #c678dd;line-height: 26px;">int</span>&nbsp;toIndex)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;list.subList(fromIndex,&nbsp;toIndex);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br><br>&nbsp;<span style="color: #61aeee;line-height: 26px;">@PostMapping</span>(<span style="color: #98c379;line-height: 26px;">"checkValidList"</span>)<br>&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;String&nbsp;<span style="color: #61aeee;line-height: 26px;">checkValidList</span><span style="line-height: 26px;">(@RequestBody&nbsp;@Valid&nbsp;ValidList&lt;User&gt;&nbsp;users)</span>&nbsp;</span>{<br>&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;<span style="color: #98c379;line-height: 26px;">"ok"</span>;<br>&nbsp;}<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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/nibxxlib1VaPfcUVibsaVCdpT5SQY72cdbDEpQtw1lOols8Dskz7WPOFr8GfeCBXgJCfeZxACuHXJegadicO6icib3ib7N5j1aMiaEv5/640?wx_fmt=svg&quot;) 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>&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">class</span>&nbsp;<span style="color: #e6c07b;line-height: 26px;">ParamTestController</span>&nbsp;</span>{<br>&nbsp;<span style="color: #61aeee;line-height: 26px;">@GetMapping</span>(<span style="color: #98c379;line-height: 26px;">"checkParam"</span>)<br>&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;String&nbsp;<span style="color: #61aeee;line-height: 26px;">checkParam</span><span style="line-height: 26px;">(@RequestParam&nbsp;&nbsp;@Max(value&nbsp;=&nbsp;<span style="color: #d19a66;line-height: 26px;">99</span>,&nbsp;message&nbsp;=&nbsp;<span style="color: #98c379;line-height: 26px;">"不能大于99岁"</span>)</span>&nbsp;Integer&nbsp;age)&nbsp;</span>{<br>&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;<span style="color: #98c379;line-height: 26px;">"ok"</span>;<br>&nbsp;}<br><br>&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@GetMapping</span>(<span style="color: #98c379;line-height: 26px;">"checkPath/{id}"</span>)<br>&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;String&nbsp;<span style="color: #61aeee;line-height: 26px;">checkPath</span><span style="line-height: 26px;">(@PathVariable&nbsp;&nbsp;@Pattern(regexp&nbsp;=&nbsp;<span style="color: #98c379;line-height: 26px;">"^[0-9]*$"</span>,&nbsp;message&nbsp;=&nbsp;<span style="color: #98c379;line-height: 26px;">"id参数值必须是正整数"</span>)</span>&nbsp;String&nbsp;id)&nbsp;&nbsp;&nbsp;</span>{<br>&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;<span style="color: #98c379;line-height: 26px;">"ok"</span>;<br>&nbsp;}<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>

12.9k star,最强链路监控系统推荐,推荐

作者:微信小助手

<p style="text-align: center;margin-bottom: 0px;"><span style="color: rgb(115, 250, 121);">点击上方蓝字&nbsp;</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);">&nbsp;关注大侠之运维</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, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, 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, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, 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, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, 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==&amp;mid=2247485614&amp;idx=1&amp;sn=93411899429a548ecd9f378f00722db7&amp;chksm=fdb5fcb2cac275a4318bd730f017b59c3afedf2964ebae026b2a8d4ffed81cf6bbf4292bbfbf&amp;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==&amp;mid=2247485647&amp;idx=1&amp;sn=9f1fffd1f406fbfebd3be499114c8943&amp;chksm=fdb5fcd3cac275c5fc560d752a16d09d68cb9c479a7dd863e366ab180c3f3c5ca224e2a8fbd8&amp;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==&amp;mid=2247485708&amp;idx=1&amp;sn=ccbf3d0ce863cf65d0a1d309bf8370d5&amp;chksm=fdb5fd10cac274068327f2a841aed675bdc0131dcc02a5e1d2acf2f3e7e922618375098d3674&amp;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==&amp;mid=2247486699&amp;idx=1&amp;sn=e437eaead5d6db10fd1dfad5da5189a1&amp;chksm=fdb5f8f7cac271e111819744fb554a759e5fdd404b32878b161b41f524fe88344e3f170c3434&amp;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;">&nbsp; &nbsp; &nbsp; &nbsp;</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, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, 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, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, 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>

SpringBoot+Nginx实现视频在线点播

作者:微信小助手

<section> <section> <p><span style="font-size: 14px;font-family: 微软雅黑, Microsoft YaHei;color: rgb(0, 0, 0);"><span style="font-family: 微软雅黑;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);">原创声明:本人所发内容及涉及源码,均为亲手所撸,如总结内容有误,欢迎指出</span></span><span style="color: rgb(0, 0, 0);font-family: 微软雅黑, &quot;Microsoft YaHei&quot;;font-size: 14px;letter-spacing: 1.5px;background-color: rgb(255, 255, 255);"></span></p> </section> <section data-id="131962" data-tools="速排小蚂蚁编辑器" powered-by="xmyeditor.com" data-md5="59fa2"> <section style="text-align: center;" powered-by="xmyeditor.com" data-md5="59fa2"> <section style="display: inline-block;vertical-align: middle;" powered-by="xmyeditor.com" data-md5="59fa2"> <section style="display: flex;align-items: stretch;justify-content: flex-start;padding-left: 18px;margin-bottom: -4px;" powered-by="xmyeditor.com" data-md5="59fa2"> <section style="background-color: rgb(255, 208, 64);width: 7px;height: 8px;transform: skew(-30deg);" powered-by="xmyeditor.com" data-md5="59fa2"> <br> </section> <section style="background-color: rgb(255, 208, 64);width: 7px;height: 8px;margin-right: 5px;margin-left: 5px;transform: skew(-30deg);" powered-by="xmyeditor.com" data-md5="59fa2"> <br> </section> <section style="background-color: rgb(255, 208, 64);width: 7px;height: 8px;transform: skew(-30deg);" powered-by="xmyeditor.com" data-md5="59fa2"> <br> </section> </section> <section style="padding-right: 23px;padding-left: 23px;background-color: rgb(92, 142, 254);border-radius: 18px;" powered-by="xmyeditor.com" data-md5="59fa2"> <section style="color: rgb(255, 255, 255);letter-spacing: 2px;margin-left: 2px;font-weight: bold;line-height: 36px;font-size: 16px;" powered-by="xmyeditor.com" data-md5="59fa2"> <p data-md5="59fa2">唠嗑部分</p> </section> </section> </section> </section> </section> <section> <section> <p cid="n9" mdtype="paragraph" style="line-height: 1.6rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-family: &quot;JetBrains Mono&quot;, Menlo, Consolas;font-size: 16px;letter-spacing: normal;text-align: start;"><span style="color: rgb(0, 0, 0);font-family: 微软雅黑;font-size: 14px;letter-spacing: 0.544px;orphans: 2;text-align: justify;word-spacing: 0px;background-color: rgb(255, 255, 255);float: none;display: inline !important;">视频点播方案如下:</span></p> <p cid="n10" mdtype="paragraph" style="line-height: 1.6rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-family: &quot;JetBrains Mono&quot;, Menlo, Consolas;font-size: 16px;letter-spacing: normal;text-align: start;"><span style="color: rgb(0, 0, 0);font-family: 微软雅黑;font-size: 14px;letter-spacing: 0.544px;orphans: 2;text-align: justify;word-spacing: 0px;background-color: rgb(255, 255, 255);float: none;display: inline !important;">1.播放器通过 http协议从http服务器上下载视频文件进行播放</span></p> <p cid="n11" mdtype="paragraph" style="line-height: 1.6rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-family: &quot;JetBrains Mono&quot;, Menlo, Consolas;font-size: 16px;letter-spacing: normal;text-align: start;"><span style="color: rgb(0, 0, 0);font-family: 微软雅黑;font-size: 14px;letter-spacing: 0.544px;orphans: 2;text-align: justify;word-spacing: 0px;background-color: rgb(255, 255, 255);float: none;display: inline !important;">问题:必须等到视频下载完才可以播放,不支持快进到某个时间点进行播放</span></p> <p cid="n12" mdtype="paragraph" style="line-height: 1.6rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-family: &quot;JetBrains Mono&quot;, Menlo, Consolas;font-size: 16px;letter-spacing: normal;text-align: start;"><span style="color: rgb(0, 0, 0);font-family: 微软雅黑;font-size: 14px;letter-spacing: 0.544px;orphans: 2;text-align: justify;word-spacing: 0px;background-color: rgb(255, 255, 255);float: none;display: inline !important;">2.播放器通过rtmp协议连接媒体服务器以实时流方式播放视频</span></p> <p cid="n13" mdtype="paragraph" style="line-height: 1.6rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-family: &quot;JetBrains Mono&quot;, Menlo, Consolas;font-size: 16px;letter-spacing: normal;text-align: start;"><span style="color: rgb(0, 0, 0);font-family: 微软雅黑;font-size: 14px;letter-spacing: 0.544px;orphans: 2;text-align: justify;word-spacing: 0px;background-color: rgb(255, 255, 255);float: none;display: inline !important;">使用rtmp协议需要架设媒体服务器,造价高,对于直播多采用此方案。</span></p> <p cid="n14" mdtype="paragraph" style="line-height: 1.6rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-family: &quot;JetBrains Mono&quot;, Menlo, Consolas;font-size: 16px;letter-spacing: normal;text-align: start;"><span style="color: rgb(0, 0, 0);font-family: 微软雅黑;font-size: 14px;letter-spacing: 0.544px;orphans: 2;text-align: justify;word-spacing: 0px;background-color: rgb(255, 255, 255);float: none;display: inline !important;">3.播放器使用HLS协议连接http服务器(Nginx、Apache等)实现近实时流方式播放视频</span></p> <p cid="n15" mdtype="paragraph" style="line-height: 1.6rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-family: &quot;JetBrains Mono&quot;, Menlo, Consolas;font-size: 16px;letter-spacing: normal;text-align: start;"><span style="color: rgb(0, 0, 0);font-family: 微软雅黑;font-size: 14px;letter-spacing: 0.544px;orphans: 2;text-align: justify;word-spacing: 0px;background-color: rgb(255, 255, 255);float: none;display: inline !important;">HLS协议规定:基于Http协议,视频封装格式为ts,视频的编码格式为H264,音频编码格式为MP3、AAC或者AC-3</span></p> <p cid="n17" mdtype="paragraph" style="line-height: 1.6rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-family: &quot;JetBrains Mono&quot;, Menlo, Consolas;font-size: 16px;letter-spacing: normal;text-align: start;"><span style="color: rgb(0, 0, 0);font-family: 微软雅黑;font-size: 14px;letter-spacing: 0.544px;orphans: 2;text-align: justify;word-spacing: 0px;background-color: rgb(255, 255, 255);float: none;display: inline !important;">什么是HLS?</span></p> <p cid="n24" mdtype="paragraph" style="line-height: 1.6rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-family: &quot;JetBrains Mono&quot;, Menlo, Consolas;font-size: 16px;letter-spacing: normal;text-align: start;"><span style="color: rgb(0, 0, 0);font-family: 微软雅黑;font-size: 14px;letter-spacing: 0.544px;orphans: 2;text-align: justify;word-spacing: 0px;background-color: rgb(255, 255, 255);float: none;display: inline !important;">HLS的工作方式是:将视频拆分成若干ts格式的小文件,通过m3u8格式的索引文件对这些ts小文件建立索引。一般10秒一个ts文件,播放器连接m3u8文件播放,当快进时通过m3u8即可找到对应的索引文件,并去下载对应的ts文件,从而实现快进、快退以近实时的方式播放视频。</span></p> <p cid="n25" mdtype="paragraph" style="line-height: 1.6rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-family: &quot;JetBrains Mono&quot;, Menlo, Consolas;font-size: 16px;letter-spacing: normal;text-align: start;"><span style="color: rgb(0, 0, 0);font-family: 微软雅黑;font-size: 14px;letter-spacing: 0.544px;orphans: 2;text-align: justify;word-spacing: 0px;background-color: rgb(255, 255, 255);float: none;display: inline !important;">IOS、Android设备、及各大浏览器都支持HLS协议。</span></p> <p cid="n29" mdtype="paragraph" style="line-height: 1.6rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-family: &quot;JetBrains Mono&quot;, Menlo, Consolas;font-size: 16px;letter-spacing: normal;text-align: start;"><span style="color: rgb(0, 0, 0);font-family: 微软雅黑;font-size: 14px;letter-spacing: 0.544px;orphans: 2;text-align: justify;word-spacing: 0px;background-color: rgb(255, 255, 255);float: none;display: inline !important;">今天我们使用SpringBoot+Nginx实现一下视频在线点播</span></p> </section> </section> <section style="color: rgb(0, 0, 0);"> <section style="font-family: 微软雅黑;font-size: 16px;letter-spacing: 1px;"> <section data-id="131962" data-tools="速排小蚂蚁编辑器" powered-by="xmyeditor.com" data-md5="59fa2"> <section style="text-align: center;" powered-by="xmyeditor.com" data-md5="59fa2"> <section style="display: inline-block;vertical-align: middle;" powered-by="xmyeditor.com" data-md5="59fa2"> <section style="display: flex;align-items: stretch;justify-content: flex-start;padding-left: 18px;margin-bottom: -4px;" powered-by="xmyeditor.com" data-md5="59fa2"> <section style="background-color: rgb(255, 208, 64);width: 7px;height: 8px;transform: skew(-30deg);" powered-by="xmyeditor.com" data-md5="59fa2"> <br> </section> <section style="background-color: rgb(255, 208, 64);width: 7px;height: 8px;margin-right: 5px;margin-left: 5px;transform: skew(-30deg);" powered-by="xmyeditor.com" data-md5="59fa2"> <br> </section> <section style="background-color: rgb(255, 208, 64);width: 7px;height: 8px;transform: skew(-30deg);" powered-by="xmyeditor.com" data-md5="59fa2"> <br> </section> </section> <section style="padding-right: 23px;padding-left: 23px;background-color: rgb(92, 142, 254);border-radius: 18px;" powered-by="xmyeditor.com" data-md5="59fa2"> <section style="color: rgb(255, 255, 255);letter-spacing: 2px;margin-left: 2px;font-weight: bold;line-height: 36px;" powered-by="xmyeditor.com" data-md5="59fa2"> <p data-md5="59fa2">言归正传</p> </section> </section> </section> </section> </section> <section> <p cid="n5" mdtype="paragraph" style="line-height: 1.6rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-family: &quot;JetBrains Mono&quot;, Menlo, Consolas;letter-spacing: normal;text-align: start;"><span style="font-family: 微软雅黑;font-size: 15px;"><strong>1、ffmpeg的使用</strong></span></p> <p cid="n34" mdtype="paragraph" style="line-height: 1.6rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-family: &quot;JetBrains Mono&quot;, Menlo, Consolas;letter-spacing: normal;text-align: start;"><span style="color: rgb(0, 0, 0);font-family: 微软雅黑;font-size: 14px;letter-spacing: 0.544px;orphans: 2;text-align: justify;word-spacing: 0px;background-color: rgb(255, 255, 255);float: none;display: inline !important;">FFmpeg被许多开源项目采用,QQ影音、暴风影音、VLC等。</span></p> <p cid="n35" mdtype="paragraph" style="line-height: 1.6rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-family: &quot;JetBrains Mono&quot;, Menlo, Consolas;letter-spacing: normal;text-align: start;"><span style="color: rgb(0, 0, 0);font-family: 微软雅黑;font-size: 14px;letter-spacing: 0.544px;orphans: 2;text-align: justify;word-spacing: 0px;background-color: rgb(255, 255, 255);float: none;display: inline !important;">下载:FFmpeg &nbsp;</span><span style="color: rgb(0, 0, 0);font-family: 微软雅黑;font-size: 14px;letter-spacing: 0.544px;orphans: 2;text-align: justify;word-spacing: 0px;background-color: rgb(255, 255, 255);float: none;display: inline !important;">https://www.ffmpeg.org/download.html#build-windows</span></p> <p cid="n36" mdtype="paragraph" style="line-height: 1.6rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-family: &quot;JetBrains Mono&quot;, Menlo, Consolas;letter-spacing: normal;text-align: start;"><span style="color: rgb(0, 0, 0);font-family: 微软雅黑;font-size: 14px;letter-spacing: 0.544px;orphans: 2;text-align: justify;word-spacing: 0px;background-color: rgb(255, 255, 255);float: none;display: inline !important;">将安装们目录配置于环境变量之中</span></p> <p cid="n37" mdtype="paragraph" style="line-height: 1.6rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-family: &quot;JetBrains Mono&quot;, Menlo, Consolas;letter-spacing: normal;text-align: start;"><span md-inline="image" src="https://gitee.com/cxs_git_com/imgs_repository/raw/master/imgs/image-20230714170506951.png" style="min-width: 10px;min-height: 10px;word-break: break-all;font-family: monospace;vertical-align: top;display: inline-block;width: 1080px;"><img class="rich_pages wxw-img" data-ratio="0.3748743718592965" src="/upload/4837f4e0b069514705d1d038435aa55e.png" data-type="png" data-w="995" style="border-width: 0px 4px 0px 2px;border-top-style: initial;border-right-style: solid;border-bottom-style: initial;border-left-style: solid;border-top-color: initial;border-right-color: transparent;border-bottom-color: initial;border-left-c