go 学习笔记之咬文嚼字带你弄清楚 defer 延迟函数
温故知新不忘延迟基础
A "defer" statement invokes a function whose execution is deferred to the moment the surrounding function returns, either because the surrounding function executed a return statement, reached the end of its function body, or because the corresponding goroutine is panicking.
延迟函数的运行时机一般有三种情况:
- 周围函数遇到返回时
func funcWithMultipleDeferAndReturn() { defer fmt.Println(1) defer fmt.Println(2) fmt.Println(3) return fmt.Println(4) }
> 运行结果: 3 2 1
. > > 「雪之梦技术驿站」: defer fmt.Println(1)
和 defer fmt.Println(2)
两个语句由于前面存在 defer
关键字,因此均被延迟到正常语句 return
前.当多个 defer
语句均被延迟时,倒序执行延迟语句,这种特点非常类似于数据结构的栈(先入后出).所以依次输出 fmt.Println(3)
,defer fmt.Println(2)
,defer fmt.Println(1)
.
- 周围函数函数体结尾处
func funcWithMultipleDeferAndEnd() { defer fmt.Println(1) defer fmt.Println(2) fmt.Println(3) }
> 运行结果: 3 2 1
. > > 「雪之梦技术驿站」: 比 funcWithMultipleDeferAndReturn
示例简单一些,虽然包围函数 funcWithMultipleDeferAndEnd
并没有显示声明 return
语句,但是当函数运行结束前依然不会忘记执行延迟语句.所以 fmt.Println(3)
执行完后,程序并没有立即结束而是紧接着执行延迟语句 defer fmt.Println(2)
和 defer fmt.Println(1)
.
- 当前协程惊慌失措中
func funcWithMultipleDeferAndPanic() { defer fmt.Println(1) defer fmt.Println(2) fmt.Println(3) panic("panic") fmt.Println(4) }
> 运行结果: 3 2 1
. > > 「雪之梦技术驿站」: 和 funcWithMultipleDeferAndReturn
示例有点类似,只不过由原来的 return
语句换成了 panic("panic")
. 我们知道延迟语句 defer fmt.Println(1)
和 defer fmt.Println(2)
肯定会被延迟执行,所以并不会先输出 1,2
而是先执行了 fmt.Println(3)
,下一步就遇到了 panic("panic")
,此时顾不上惊慌失措,先让已存在的 defer
语句先执行再说! > 同时,defer
是倒序执行的,因而先输出 defer fmt.Println(2)
再输出 defer fmt.Println(1)
,最后完成使命,光荣挂掉,至于 fmt.Println(4)
就无法执行了!
关于这一句话的详细解读,请参考 go 学习笔记之解读什么是defer延迟函数,示例源码见 snowdreams1006/learn-go/tree/master/error
如果你真的试图去理解 defer
的执行时机,最好看一下汇编代码的具体实现,推荐一下大佬的 defer关键字
关于 defer
关键字相关解释,摘录如下:
> 当函数包含 defer
语句,则汇编代码:
> c > call runtime.deferreturn, > add xx SP > return
> goroutine
的控制结构中,有一张表记录 defer
,调用 runtime.deferproc
时会将需要 defer
的表达式记录在表中,而在调用 runtime.deferreturn
的时候,则会依次从 defer
表中出栈并执行。
但是,从语义上理解会更加简单,问一下自己为什么需要 defer
关键字,到底解决了什么问题?
一旦理解了 defer
关键字的实现意图,那么自然而然就能大概猜出有关执行顺序,所以何必深究实现细节呢?
简而言之,defer
关键字是确保程序一定会执行的代码逻辑,不管程序是正常 return
还是意外 panic
,包围函数一旦存在 defer
关键字就要保证延迟函数一定执行!
当存在多个 defer
关键字时,意味着有多个紧急任务需要处理,时间紧迫,当然是事故发生点最近的优先执行,离return
或 panic
越远的越晚执行.
所以以防万一和就近原则是理解 defer
执行时机的最佳途径: 万一哪天发生火灾,第一反应自然是就近救人啊!
支持什么又不支持哪些
The expression must be a function or method call; it cannot be parenthesized. Calls of built-in functions are restricted as for expression statements.
- 支持函数调用
func funcCallWithDefer() { fmt.Println("funcInvokeWithDefer function is called") } func TestFuncCallWithDefer(t *testing.T) { // 「雪之梦技术驿站」: defer 语句可以是函数调用. fmt.Println(" 「雪之梦技术驿站」: defer 语句可以是函数调用.") defer funcCallWithDefer() fmt.Println("TestFuncInvokeWithDefer function call has ended") }
- 支持方法调用
type Lang struct { name string website string } func (l *Lang) ToString() { fmt.Printf("Lang:[name = %s,website = %s] \n", l.name, l.website) } func TestMethodCallWithDefer(t *testing.T) { // 「雪之梦技术驿站」: defer 语句也可以是方法调用. fmt.Println(" 「雪之梦技术驿站」: defer 语句也可以是方法调用.") var l = new(Lang) l.name = "Go" l.website = "https://snowdreams1006.github.io/go/" defer l.ToString() fmt.Println("TestMethodCallWithDefer method call has ended") }
- 不可以被括号包裹
- 内建函数和表达式一样受限
函数名 | 说明 | 说明 |
---|---|---|
close | 关闭channel | 仅用于channel通讯 |
delete | 从map中删除实例 | map操作 |
len | 返回字符串,slice和数组的长度 | 可用于不同的类型 |
cap | 返回容量 | 可用于不同的类型 |
new | 内存分配 | 用于各种类型 |
make | 内存分配 | 仅用于chan/slice/map |
copy | 复制slice | slice操作 |
append | 追加slice | slice操作 |
panic | 报告运行时问题 | 异常处理机制 |
recover | 处理运行时问题 | 异常处理机制 |
内建打印函数 | 主要用于不引入fmt的时候的调试,实际使用时建议使用标准库fmt | |
println | 内建打印函数 | 主要用于不引入fmt的时候的调试,实际使用时建议使用标准库fmt |
complex | 构造复数类型 | 复数操作 |
real | 抽出复数的实部 | 复数操作 |
imag | 抽出复数的虚部 | 复数操作 |
func TestBuiltinFuncCallWithDefer(t *testing.T) { // 「雪之梦技术驿站」: defer 语句不可以被括号包裹. fmt.Println(" 「雪之梦技术驿站」: defer 语句不可以被括号包裹.") arr := new([10]int) arr[4] = 5 arr[7] = 8 // defer discards result of len(arr) defer len(arr) defer println("Calls of built-in functions are restricted as for expression statements.") fmt.Println("TestBuiltinFuncCallWithDefer function call has ended") }
咬文嚼字深入理解延迟
Each time a "defer" statement executes, the function value and parameters to the call are evaluated as usual and saved anew but the actual function is not invoked. Instead, deferred functions are invoked immediately before the surrounding function returns, in the reverse order they were deferred. That is, if the surrounding function returns through an explicit return statement, deferred functions are executed after any result parameters are set by that return statement but before the function returns to its caller. If a deferred function value evaluates to nil, execution panics when the function is invoked, not when the "defer" statement is executed.
打蛇打七寸
> Each time a "defer" statement executes, the function value and parameters to the call are evaluated as usual and saved anew but the actual function is not invoked.
Each time a "defer" statement executes, the function value and parameters to the call are evaluated as usual and saved anew but the actual function is not invoked.
每次延迟语句执行时,函数值和调用参数会像以往一样被评估和保存,但是实际函数并不会被调用.
func trace(funcName string) func(){ start := time.Now() fmt.Printf("function %s enter at %s \n",funcName,start) return func(){ fmt.Printf("function %s exit at %s(elapsed %s)",funcName,time.Now(),time.Since(start)) } } func foo(){ fmt.Printf("foo begin at %s \n",time.Now()) defer trace("foo")() time.Sleep(5*time.Second) fmt.Printf("foo end at %s \n",time.Now()) } func TestFoo(t *testing.T) { foo() }
trace
函数实现了函数计时功能,而 foo
函数则是包围函数用于演示 defer
关键字的逻辑,TestFoo
是测试函数,输出测试结果.
测试结果如下:
> === RUN TestFoo > foo begin at 2019-11-18 23:12:38.519097 +0800 CST m=+0.000735902 > function foo enter at 2019-11-18 23:12:38.519287 +0800 CST m=+0.000926011 > foo end at 2019-11-18 23:12:43.524445 +0800 CST m=+5.005934027 > function foo exit at 2019-11-18 23:12:43.524549 +0800 CST m=+5.006038281(elapsed > 5.005112612s)--- PASS: TestFoo (5.01s) > PASS > > Process finished with exit code 0
如果此时试图去解释上述运行结果,很遗憾铩羽而归!
记得官方文档中关于 defer
描述的第一句话就阐明了延迟函数的执行时机,原文如下:
A "defer" statement invokes a function whose execution is deferred to the moment the surrounding function returns, either because the surrounding function executed a return statement, reached the end of its function body, or because the corresponding goroutine is panicking.
但是如果按照这句话来解释此次示例的运行结果,显然是解释不通的!
func foo(){ fmt.Printf("foo begin at %s \n",time.Now()) defer trace("foo")() time.Sleep(5*time.Second) fmt.Printf("foo end at %s \n",time.Now()) } func TestFoo(t *testing.T) { foo() }
如果 defer trace("foo")()
延迟函数真的被延迟到函数体结束之前,那么上述 foo()
函数应该等价于这种形式:
func fooWithoutDefer(){ fmt.Printf("foo begin at %s \n",time.Now()) time.Sleep(5*time.Second) fmt.Printf("foo end at %s \n",time.Now()) trace("foo")() } func TestFooWithoutDefer(t *testing.T) { fooWithoutDefer() }
但是对于 fooWithoutDefer
函数的执行结果直接实力打脸:
> === RUN TestFooWithoutDefer > foo begin at 2019-11-19 11:44:20.066554 +0800 CST m=+0.001290523 > foo end at 2019-11-19 11:44:25.068724 +0800 CST m=+5.003312582 > function foo enter at 2019-11-19 11:44:25.068796 +0800 CST m=+5.003384341 > function foo exit at 2019-11-19 11:44:25.068847 +0800 CST m=+5.003435185(elapsed 51.196µs)--- PASS: TestFooWithoutDefer (5.00s) > PASS > > Process finished with exit code 0
由此可见,延迟函数其实并不简单,想要弄清楚 defer
关键字还要继续读下去才有可能!
这一点也是我最大的疑惑,潜意识告诉我: 只要无法真正理解 Each time a "defer" statement executes, the function value and parameters to the call are evaluated as usual and saved anew but the actual function is not invoked.
这句话的含义,那么永远不可能彻底弄清 defer
关键字!
通过直接调换 defer
语句的出现位置并没有解释测试结果,反而告诉我们 defer
语句可不是简简单单的延迟执行.
任何函数都会或多或少依赖相应的执行环境,defer
延迟函数也不例外,在本示例中 defer trace("foo")()
延迟函数的 trace("foo")
函数的返回值是函数,然后 trace("foo")()
相当于立即执行返回函数,因而问题可能出现在 trace("foo")
函数中,那么不妨继续看看吧!
func foo(){ fmt.Printf("foo begin at %s \n",time.Now()) defer trace("foo")() time.Sleep(5*time.Second) fmt.Printf("foo end at %s \n",time.Now()) } func trace(funcName string) func(){ start := time.Now() fmt.Printf("function %s enter at %s \n",funcName,start) return func(){ fmt.Printf("function %s exit at %s(elapsed %s)",funcName,time.Now(),time.Since(start)) } }
1. foo begin at 2019-11-19 14:06:42.385982 +0800 CST m=+0.000943615
暗示着已经开始进入 foo()
函数内部,接下来的 function foo enter at 2019-11-19 14:06:42.38623 +0800 CST m=+0.001191025
意味着函数并没有执行 time.Sleep(5*time.Second)
而是直接进入了 defer trace("foo")()
语句内部,可见函数依旧是顺序执行,但是 trace(funcName string) func()
函数内部会返回函数,此时函数返回值并没有执行,因为此时并不存在打印输出的日志.
所以 trace(funcName string) func()
函数应该是已经执行了,接下来返回上一层回到主函数 foo()
就遇到了 time.Sleep(5*time.Second)
休息 5s
语句,所以在执行 fmt.Printf("foo end at %s \n",time.Now())
语句时输出的时间和最近的上一句差了大概 5s
.
foo end at 2019-11-19 14:06:47.391581 +0800 CST m=+5.006394415
输出后也就意味着 foo()
函数运行到包围函数的结束处,此时按照延迟语句的第一句,我们知道是时候执行真正的延迟逻辑了.
所以下一句就是 trace("foo")()
的函数返回值的调用,输出了 function foo exit at 2019-11-19 14:06:47.391706 +0800 CST m=+5.006518615(elapsed 5.005327927s)--- PASS: TestFoo (5.01s)
至此,延迟函数执行完毕,单元测试函数也输出了 PASS
.
=== RUN TestFoo foo begin at 2019-11-19 14:06:42.385982 +0800 CST m=+0.000943615 function foo enter at 2019-11-19 14:06:42.38623 +0800 CST m=+0.001191025 foo end at 2019-11-19 14:06:47.391581 +0800 CST m=+5.006394415 function foo exit at 2019-11-19 14:06:47.391706 +0800 CST m=+5.006518615(elapsed 5.005327927s)--- PASS: TestFoo (5.01s) PASS
通过上述分析,可以这么理解,延迟函数也是需要执行环境的,而执行环境就是依赖于定义 defer
语句时的相关环境,这也就是延迟函数的准备阶段或者说入栈.
当遇到包围函数体返回时或到达包围函数体结尾处或发生错误时,包围函数就会调用已存在的延迟函数,这部分就是延迟函数的执行阶段或者说出栈.
- 无论是否存在延迟函数,均顺序执行函数逻辑
- 准备阶段的入栈操作会正常运行但不会调用函数
- 执行阶段的出栈操作在合适时机时会调用函数
同样地,仍然以消防队员作为 Go
的调度器,平民百姓作为无 defer
保护的对比参考,而有 defer
保护的特殊人群作为延迟函数.
有一天,普通百姓和特殊人士都在商场逛街,突发火灾,附近消防员迅速赶紧救人,任务只要一个:那就是按照就近原则快速救出全部特殊人群,因为这些特殊人群都是有头有脸的人物,每个人都有自己的脾气个性.
明星 A : 我进商场前拿着限量版的 LV
包包,这个我也要拿出去! 富二代 B : 我进商场前答应小女友要给他买个礼物,这个是寄存柜地址,别忘了把礼物也带回来! 暴发户 C : 我在商场有个保险柜,存放了大量金条,一定要给我带出去!
消防员很无奈,心里咒骂了一句: 这都生死攸关了,还管什么身外之物啊!
可是,埋怨归埋怨,对于这些特殊人群的照顾,那是一丁点也不敢怠慢,只能照办,终于全部救出了!
> A 表示声明 defer
语句时已经传递了参数,等到执行 defer
时调用的就是刚才的参数值,而 Go
语言中参数的传递只能是值传递,所以虽然看起来还是那个包,其实已经变了,这里并不是特别准确! > B 表示声明 defer
语句时传递的参数不是具体值而是引用,当执行 defer
逻辑时会按图索骥,因此虽然给的是一张寄存柜的密码纸,最后拿出来的却是存在柜子里的礼物. > C 表示声明 defer
时什么都没有传递,没有任何入参但是执行 defer
语句中遇到了访问包围函数的需求,这时候延迟函数会扩大搜索范围向上寻找直到找到商场的金库为止.
- 零依赖而无顾虑
func deferWithoutParams() { // 2 1 defer fmt.Println(1) fmt.Println(2) }
> 「雪之梦技术驿站」: 入栈时没有任何依赖,出栈时也不会有任何顾虑,非常简单直观输出了 2 1
.
- 随身携带的牵挂
func deferWithValueParams() { x := 10 defer func(n int) { // 10 fmt.Println(n) }(x) x++ }
> 「雪之梦技术驿站」: 入栈时存在值参数 func(n int)(10)
,出栈时需要输出参数的值,而 fmt.Println(n)
涉及到的 n
刚好保存在入栈环境中,所以等到 deferWithValueParams
运行到函数结束后输出的结果就是已缓存的副本 10
.
如果此时匿名函数调用的不是 n
而是 x
,而变量 x
并不存在于入栈环境中,此时就会继续扩大范围搜到 deferWithValueParams
函数是否存在变量 x
的声明,本示例中找到的 x=11
.
func deferWithOuterParams() { x := 10 defer func(n int) { // 11 fmt.Println(x) }(x) x++ }
- 心有牵挂放不下
func deferWithReferParams() { x := 10 defer func(n *int) { // 11 fmt.Println(*n) }(&x) x++ }
> 「雪之梦技术驿站」: 入栈时保存的不再是值而是地址,因此出栈时会按图索骥,找到该地址对应的值,也就是 11
.
相信以上案例应该帮助读者理解 defer
语句的一些注意事项了吧?
延迟函数准备阶段的入栈会收集函数运行所需的环境依赖,比如说入参的值,收集结束后即使外界再改变该值也不会影响延迟函数,因为延迟函数用的是缓存副本啊!
出栈会倒序
> Instead, deferred functions are invoked immediately before the surrounding function returns, in the reverse order they were deferred.
相反的,延迟函数会在包围函数返回之前按照被延迟顺序逆序调用.
func TestFuncWithMultipleDefer(t *testing.T) { // 「雪之梦技术驿站」: 猜测 defer 底层实现数据结构可能是栈,先进后出. t.Log(" 「雪之梦技术驿站」: 猜测 defer 底层实现数据结构可能是栈,先进后出.") // 3 2 1 defer t.Log(1) defer t.Log(2) t.Log(3) }
> 「雪之梦技术驿站」: 运行阶段的出栈操作会倒序执行多个 defer
延迟函数,所以输出了 3 2 1
.
及时雨插入
> That is, if the surrounding function returns through an explicit return statement, deferred functions are executed after any result parameters are set by that return statement but before the function returns to its caller.
当包围函数通过明确的 return
返回语句返回时,defer
延迟函数会在 result parameters
结果参数被赋值之后且在函数 return
返回之前执行.
按照这句话可以将下面这种代码进行拆解:
defer yyy return xxx
其中 return xxx
相当于拆开了两步并且最终返回前及时插入了 defer
语句的执行逻辑,如下:
1. result parameters = xxx 2. 调用 defer 函数 3. return
同样地,我们举例说明:
func deferWithExplicitReturn() (result int) { defer func() { // 2. before : result = 10 fmt.Printf("before : result = %v\n", result) result++ // 3. after : result = 11 fmt.Printf("after : result = %v\n", result) }() result = 10 // 1. return : result = 10 fmt.Printf("return : result = %v\n", result) return result }
关于 defer
延迟函数的执行顺序和输出结果已经不再是难点了,现在主要关注下 deferWithExplicitReturn()
函数运行结束后的返回值到底是 10
还是 11
.
func TestDeferWithExplicitReturn(t *testing.T) { // TestDeferWithExplicitReturn result = 11 fmt.Printf("TestDeferWithExplicitReturn result = %d\n",deferWithExplicitReturn()) }
> 「雪之梦技术驿站」: 测试结果输出了 11
,很显然这里是因为延迟函数内部执行了 result++
操作最终影响了外部函数的返回值.
如果对上述示例进行改造,下面的代码就清晰看出了为什么会影响返回值了.
func deferWithExplicitReturnByExplain() (result int) { result = 10 // 1. return : result = 10 fmt.Printf("return : result = %v\n", result) func() { // 2. before : result = 10 fmt.Printf("before : result = %v\n", result) result++ // 3. after : result = 11 fmt.Printf("after : result = %v\n", result) }() return }
> 「雪之梦技术驿站」: 延迟函数会在 return
返回前有机会对返回值进行更改,这里演示了及时雨插入的逻辑,输出结果不变还是 11
.
下面提供一些例题,请自行思考
func surroundingFuncEvaluatedNotInvoked(init int) int { fmt.Printf("1.init=%d\n",init) defer func() { fmt.Printf("2.init=%d\n",init) init ++ fmt.Printf("3.init=%d\n",init) }() fmt.Printf("4.init=%d\n",init) return init } func noDeferFuncOrderWhenReturn() (result int) { func() { // 1. before : result = 0 fmt.Printf("before : result = %v\n", result) result++ // 2. after : result = 1 fmt.Printf("after : result = %v\n", result) }() // 3. return : result = 1 fmt.Printf("return : result = %v\n", result) return 0 } func deferFuncWithAnonymousReturnValue() int { var retVal int defer func() { retVal++ }() return 0 } func deferFuncWithNamedReturnValue() (retVal int) { defer func() { retVal++ }() return 0 }
> 「雪之梦技术驿站」: 如果一眼看不出答案,不妨复制到编辑器直接运行,然后在思考为什么.
调用时报错
> If a deferred function value evaluates to nil, execution panics when the function is invoked, not when the "defer" statement is executed.
如果延迟函数值为 nil
,则函数调用时发生错误异常 panic
而不是 defer
语句执行时报错.
func deferWithNil() func() { return nil } func TestDeferWithNil(t *testing.T) { fmt.Println("begin exec deferWithNil()()") defer deferWithNil()() fmt.Println("end exec deferWithNil()()") }
公布答案以及总结全文
在上篇文章中留下了两个小问题,相信看到这篇文章的人都能独立完成并自行解释了吧?
下面给出问题以及答案!
func deferFuncWithAnonymousReturnValue() int { var retVal int defer func() { retVal++ }() return 0 } func deferFuncWithNamedReturnValue() (retVal int) { defer func() { retVal++ }() return 0 } func TestDeferFuncWhenReturn(t *testing.T) { // 0 t.Log(deferFuncWithAnonymousReturnValue()) // 1 t.Log(deferFuncWithNamedReturnValue()) }
> 「雪之梦技术驿站」: deferFuncWithAnonymousReturnValue()
函数无明确的返回值参数,而 deferFuncWithNamedReturnValue()
函数已经声明了 (retVal int)
返回值,因为延迟函数并不会影响未命名的函数.
通过本文,我们知道了延迟函数的执行时机以及一些细节,关键是理解 Each time a "defer" statement executes, the function value and parameters to the call are evaluated as usual and saved anew but the actual function is not invoked.
这句话,绝对是重中之重!
简而言之,延迟函数在声明时会收集相关参数赋值拷贝一份入栈,时机合适时再从入栈环境中寻找相关环境参数,如果找不到就扩大范围寻找外层函数是否包含所需变量,执行过程也就是延迟函数的出栈.
有一个消防员专门负责保卫商场的安全,每天商场进进出出很多人流,总有一些重要人物也会来到商场购物,突然有一天,发生了火灾,正在大家惊慌失措中...
这个消防员到底干了什么才能保证重要人物安全的同时也能让他们不遭受财产损失?
请补充你的答案,感谢你的阅读与关注,下一节再见~
阅读延伸以及参考文档
- Defer_statements
- Built-in_functions
- Go语言规格说明书 之 内建函数(Built-in functions)
- go语言快速入门:内建函数(6)
- 你知道defer的坑吗?
- golang语言defer特性详解.md
- Golang之轻松化解defer的温柔陷阱
> 如果本文对你有所帮助,不用赞赏直接点赞就是最大的鼓励,顺便关注下微信公众号「 雪之梦技术驿站 」那就更好啦!
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
大数据学习路线分享大数据之基础语法
1.2.1 计算机理论介绍 1.2.2 编程基础--进制分类、进制转换进制 就是进位制。指的是我们来表示一个数字的时候进位的制度。 进制分类 计算机中,常用的进制有以下几种: 进制 描述 示例 备注 二进制 使用0和1来描述所有的自然数 0, 1, 10, 11, 100, 101 使用0b开头 八进制 使用0-7来描述所有的自然数 4, 5, 6, 7, 10, 11, 12 使用0开头 十进制 使用0-9来描述所有的自然数 6, 7, 8, 9, 10, 11, 12 十六进制 使用0-9, a-f来描述所有的自然数 9, A, B, C, D, E, F, 10 使用0x开头 进制转换 十进制转其他进制 辗转相除法,用数字除进制,再用商除进制,一直到商为零结束,最后将每一步得到的余数倒着连接其他进制转十进制 每一位的数字乘进制的位数-1次方,再将所有的结果累加到一起二进制与八进制之间的相互转换 每一个八进制位可以等价替换成三个二进制位每一个十六进制位可以等价替换成四个二进制位原码、反码、补码 正数 负数 原码 由数字直接计算出的二进制表示形式 最高位表示符号位: 0代表正数, 1...
- 下一篇
Hunt Shiro 发布 1.0.0,D 语言安全认证框架
为了进一步完善 D 语言在 Web 领域的框架生态,HuntLabs 对 Apache Shiro 进行完整的翻译,目前的实现来自 Apache Shiro 1.4.x 的设计实现。 Shiro 是一个功能强大且灵活的开源安全框架,主要功能包括用户认证、授权、会话管理以及加密。 Shiro 的首要目标是易于使用和理解。系统安全是非常复杂甚至痛苦的,但 Shiro 并不是。一个框架应该尽可能的隐藏那些复杂的细节,并且公开一组简洁直观的API以简化开发人员在系统安全上所付出的努力。 你可以通过 Shiro 完成下面这些事情: 验证用户身份 控制用户访问,例如: 确定用户是否被分配某个角色 确定用户是否被允许做某些操作 在任何环境下使用 Session API,甚至是在脱离 web 的环境下 及时响应在认证、访问控制或会话生命周期内的所有事件 从一个或多个数据源聚集用户安全数据生成一个复合的用户“视图” 实现单点登录功能 实现“记住我”功能,使用户不必每次都要登录 除了上面这些功能,还有其他很多功能,所有这些功能都集成到了一组易于使用的API中。 Shiro试图在所有应用环境实现这一目标。在...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
-
Docker使用Oracle官方镜像安装(12C,18C,19C)
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS8编译安装MySQL8.0.19
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS7,8上快速安装Gitea,搭建Git服务器
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
推荐阅读
最新文章
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS6,CentOS7官方镜像安装Oracle11G
- SpringBoot2整合Redis,开启缓存,提高访问速度
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- Hadoop3单机部署,实现最简伪集群
- MySQL8.0.19开启GTID主从同步CentOS8
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果