SpringBoot使用AOP實現REST介面簡易靈活的安全認證實踐
一、閉包函式
閉包函式=函式巢狀定義+函式物件+名稱空間與作用域
閉包函式:在函式中(巢狀)定義另一個函式時,如果內部的函式引用了外部的函式的變數,則可能產生閉包。
1、閉:指的是該函式是定義在一個函式內部的函式
2、包:值得是該函式訪問了一個來自於外層函式的變數
為函式體傳參的方法:
'''方案一:直接使用引數的形式傳遞''' def wrapper(x): print(x) wrapper(111) wrapper(222) '''方案二:把函式體想要的引數包給它(即使用閉包的概念)''' def outter(x): def wrapper(): # wrapper = 閉包函式的記憶體地址 print(x) return wrapper # 一定不要加括號 f1 = outter(111) # f = 閉包函式的記憶體地址 f2 = outter(222) # f = 閉包函式的記憶體地址 f1() f2() ----------- 111 222
乍一看會感覺使用閉包來傳引數非常的麻煩,我們之前使用函式,需要引數都是直接傳給他,方便也快捷,但是在某些場景下我們定死了某個函式無法直接傳引數,那就必須通過其他方式傳引數,即閉包的方式,下面介紹的裝飾器就是閉包的使用。
二、@符號的使用
@符號作用: (1) 可以自動把@符號下面的函式當成引數傳遞給裝飾器 (2) 把新函式進行返回,讓新函式去替換舊函式,以實現功能的擴充套件. 如: @timer # 等於func1 = timer(func1),其目的是為了讓使用者不改變呼叫方式,它依然在呼叫func1,但是實際已經被我們換成了timer def func1(): print(1)
三、疊加多個裝飾器的執行步驟
結論:(記住結論既可)
載入順序:自下而上
執行順序:自上而下執行內層的wrapper函式
驗證過程:
# 疊加多個裝飾器 def deco1(func1): # func1 = wrapper2的記憶體地址 def wrapper1(*args,**kwargs): print('wrapper1====>') res1=func1(*args,**kwargs) return res1 return wrapper1 def deco2(func2): # func2 = wrapper3的記憶體地址 def wrapper2(*args,**kwargs): print('wrapper2====>') res2=func2(*args,**kwargs) return res2 return wrapper2 def deco3(func3): # func3 = 最原始的那個被裝飾函式的記憶體地址 def wrapper3(*args,**kwargs): print('wrapper3====>') res3=func3(*args,**kwargs) return res3 return wrapper3 # index=wrapper1的記憶體地址 @deco1 # deco1(wrapper2的記憶體地址)=>wrapper1的記憶體地址 @deco2 # deco2(wrapper3的記憶體地址)=>wrapper2的記憶體地址 @deco3 # deco3(最原始的那個被裝飾函式的記憶體地址)=>wrapper3的記憶體地址 def index(x,y): print('index=>',x,y) index(1,2) ----------------------- wrapper1====>' wrapper2====> wrapper3====> index=>1,2
四、無參裝飾器
什麼是裝飾器:
器:工具
裝飾:為被裝飾者新增額外的功能
為何要有裝飾器:
軟體一旦上線執行之後,就應該遵循開放封閉原則:
1、開放指的是對拓展新功能開放
2、封閉指的是對修改原始碼封閉
這種情況下,我們在寫新功能時,若需要用到新的引數,就無法直接通過原函式來傳了,必須通過其他方式來傳參,目前我們想到的方法,是兩種傳參的另一種的方式,閉包函式
定義裝飾器的目的:
定義裝飾器就是為了在遵循1和2的前提下來為其他函式新增新功能的
ps:
不修改被裝飾物件指的是定義與呼叫都不能修改
所以下述行為都違反了開放封閉原則:
①、修改被裝飾物件定義時的原始碼
②、修改被裝飾物件的呼叫方式
如何用裝飾器:
'''無參裝飾器基本格式'''
def wrapper(func):
def inner(*args,**kwargs):
res = func(*args,**kwargs)
return func
return inner
'''
切記不能在此處加括號,加括號就代表執行,相當於先把inner執行一遍,然後拿到返回值return出去.
return 出去的值是無法加括號在執行的。
'''
# 例子
def wrapper(func):
def inner(*args,**kwargs):
res = func(*args,**kwargs)
return res
return inner() # 切記不能在此處加括號,加括號就代表執行
@wrapper
def func1():
print(11)
func1()
-----------------
11
TypeError: 'NoneType' object is not callable
'''
前面打印出來的11其實是執行wrapper裡的inner產生的,所以以上程式的執行流程是
1.先把func1傳給wrapper,然後運行了wrapper中的程式碼,返回 inner()
2.inner() 就開始執行inner函式,即運行了一遍func1,所以列印了11
3.此時的func1 = inner() 而inner()又等於func1(),func1沒有返回值,預設返回None
4.變成了None(),報錯
'''
無參裝飾器的應用場景以及構建裝飾器步驟:
需求:有一個index函式,此時我們要在index函式基礎上新增一個計算運算時間的功能,因為是公司專案,我們不能修改原函式的呼叫方式,也不能修改原始碼。
# 一、被裝飾器物件index如下
import time
def index(x,y):
time.sleep(1)
print("index---->",x,y)
index(1,2)
# 二、為index新增計算運算時間與登入功能
import time
from functools import wraps
def timmer(func):
def wrapper(*args,**kwargs):
start = time.time()
res = func(*args,**kwargs)
end = time.time()
print(end-start)
return res
return wrapper
def login(func):
def wrapper(*args,**kwargs):
name = 'yang'
pwd = '123'
inp_name = input("請輸入您的使用者名稱:").strip()
inp_pwd = input("請輸入您的密碼").strip()
if inp_name == name and inp_pwd == pwd:
print("登入成功")
res = func(*args,**kwargs)
return res
return wrapper
@timmer
@login
def index(x,y):
time.sleep(1)
print("index---->",x,y)
index(1,2)
五、有參裝飾器
對於不再需要新引數的裝飾器,兩層就可以解決了,但是當我們的裝飾器需要新的引數,如在登入的時候,我們要判斷我們使用者名稱與密碼的來源,此時需要外部將來源傳進來。而兩層的裝飾器中,第一層,是為了給原函式傳引數,他的引數不能動,而第二層,因為裝飾器語法@的原因,也已經定死了此處只能傳一個函式名,也不能傳引數,所以我們需要構造第三層來接受外部的引數,而在內部的函式可以在不改動自己依然可以獲取到外層的函式.
'''有參裝飾器模版'''
def outter2(x,y,z,a,b):
def outter1(func):
def wrapper(*args,**kwargs):
res=func(*args,**kwargs)
return res
return wrapper
return outter1
例子:
def outter(engine = 'file'):
def deco2(func2):
def wrapper2(*args,**kwargs):
inp_name = input('username>>>: ').strip()
inp_pwd = input('password>>>: ').strip()
if engine == "file":
print('基於file的認證')
if inp_name == "egon" and inp_pwd == "123":
print('login successful')
res2=func2(*args,**kwargs)
return res2
else:
print('username or password error')
elif engine == 'mysql':
print('基於mysql的認證')
elif engine == 'ldap':
print('基於ldap的認證')
else:
print('未知的engine')
return wrapper2
return deco2
@outter(engine='mysql') # @deco2 # index=deco2(index)
def index(x,y):
print('index=>',x,y)
index(1,2) # index=>wrapper
六、類裝飾器
我們也可以用類來裝飾函式
# 利用類靜態方法裝飾
class MyClass:
@staticmethod
def zhuangshi1(func):
def newfunc():
print("裝飾前")
func()
print("裝飾後")
return newfunc
def __call__(self, *args, **kwargs):
return self.zhuangshi2(*args, **kwargs)
def zhuangshi2(self,func):
def newfunc():
print('裝飾前')
func()
print('裝飾後')
return newfunc
# 利用類靜態方法,本質和原來的用函式裝飾一樣
@MyClass.zhuangshi1
def func1():
print('執行func1')
func1()
# 利用__call__方法
@MyClass()
def func2():
print('運行了func2')
func2()