spring 提供了自動代理機制,可以讓容器自動生成代理,從而把開發(fā)人員從繁瑣的配置中解脫出來 。 具體是使用 beanpostprocessor 來實現(xiàn)這項功能。
1 beanpostprocessor
beanpostprocessor 代理創(chuàng)建器的實現(xiàn)類可以分為 3 類:
類型 | 實現(xiàn)類 |
---|---|
基于 bean 配置名規(guī)則 | beannameautoproxycreator |
基于 advisor 匹配規(guī)則 | defaultadvisorautoproxycreator |
基于 bean 中的 aspectj 注解標簽的匹配規(guī)則 | annotationawareaspectjautoproxycreator |
beanpostprocessor 類繼承關系
所有的自動代理器類都實現(xiàn)了 beanpostporcessor ,在容器實例化 bean 時, beanpostprocessor 將對它進行加工處理,所以自動代理創(chuàng)建器能夠?qū)M足匹配規(guī)則的 bean 自動創(chuàng)建代理對象。
2 beannameautoproxycreator
假設有以下兩個實體類(用戶與充電寶)。
用戶類:
1
2
3
4
5
6
7
8
9
10
11
|
public class user { public void rent(string userid) { system.out.println( "user:租賃【充電寶】" ); } public void back(string userid){ system.out.println( "user:歸還【充電寶】" ); } } |
充電寶:
1
2
3
4
5
6
|
public class charger { public void rent(string userid) { system.out.println( "charger:【充電寶】被租賃" ); } } |
我們希望通過 beannameautoproxycreator 通過 bean 的名稱來自動創(chuàng)建代理,實現(xiàn)增強:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
<?xml version= "1.0" encoding= "utf-8" ?> <beans xmlns= "http://www.springframework.org/schema/beans" xmlns:xsi= "http://www.w3.org/2001/xmlschema-instance" xmlns:p= "http://www.springframework.org/schema/p" xsi:schemalocation= "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd" > <bean id= "user" class = "net.deniro.spring4.aop.user" /> <bean id= "charger" class = "net.deniro.spring4.aop.charger" /> <!-- 前置增強--> <bean id= "rentbeforeadvice" class = "net.deniro.spring4.aop.rentbeforeadvice" /> <!-- 使用 beannameautoproxycreator--> <bean class = "org.springframework.aop.framework.autoproxy.beannameautoproxycreator" p:beannames= "*er" p:interceptornames= "rentbeforeadvice" p:optimize= "true" ></bean> </beans> |
beannameautoproxycreator的 beannames 屬性允許指定一組需要自動代理的 bean 名稱, 這里可以使用 *
通配符 。
因為我們需要代理的類名分別是 user 與 charger,都是以 er 結(jié)尾的,所以我們這里定義為 *er
。
也可以通過 beannames 的 value 值來明確指定需要代理的 bean 名稱,多個以逗號分隔(更常用)。
1
2
3
|
<!-- 指定自動代理的 bean 名稱--> <property name= "beannames" value= "user,charger" > </property> |
也可以通過 list 方式來指定 beannames 的值:
1
2
3
4
5
6
|
<property name= "beannames" > <list> <value>user</value> <value>charger</value> </list> </property> |
p:optimize 設置為 true,則表示使用 cglib 動態(tài)代理技術。
通過這樣的配置之后,容器在創(chuàng)建 user 和 charger bean 的實例時,就會自動為它們創(chuàng)建代理對象,而這一操作對于使用者來說完全是透明的 。
單元測試:
1
2
3
4
5
6
|
user user = (user) context.getbean( "user" ); charger charger = (charger) context.getbean( "charger" ); string userid = "001" ; user.rent(userid); charger.rent(userid); |
輸出結(jié)果:
準備租賃的用戶 id:001
user:租賃【充電寶】
準備租賃的用戶 id:001
charger:【充電寶】被租賃
3 defaultadvisorautoproxycreator
切面 advisor 是切點和增強的復合體,而 defaultadvisorautoproxycreator 能夠掃描 advisor, 并將 advisor 自動織入到匹配的目標 bean 中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<bean id= "user" class = "net.deniro.spring4.aop.user" /> <bean id= "charger" class = "net.deniro.spring4.aop.charger" /> <!-- 前置增強--> <bean id= "rentbeforeadvice" class = "net.deniro.spring4.aop.rentbeforeadvice" /> <!-- 靜態(tài)正則表達式方法名匹配--> <bean id= "regexpadvisor" class = "org.springframework.aop.support.regexpmethodpointcutadvisor" p:advice-ref= "rentbeforeadvice" > <!-- 匹配模式--> <property name= "patterns" > <list> <!-- 匹配字符串--> <value>.*rent.*</value> </list> </property> </bean> <!-- 使用 defaultadvisorautoproxycreator--> <bean class = "org.springframework.aop.framework.autoproxy.defaultadvisorautoproxycreator" /> |
首先我們配置了以靜態(tài)正則表達式方法名匹配的切面,然后直接配置了 defaultadvisorautoproxycreator bean。
測試代碼與輸出結(jié)果與上一小節(jié)的 beannameautoproxycreator 相同。
jdk 動態(tài)代理是通過接口來實現(xiàn)方法攔截,所以必須確保要攔截的目標在接口中有定義。
cglib 動態(tài)代理是通過動態(tài)生成代理子類來實現(xiàn)方法攔截,所以必須確保要攔截的目標方法可以被子類所訪問,也就是目標方法必須定義為非 final, 且非私有實例方法 。
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:https://www.jianshu.com/p/816078a79001