作者簡介 :一只大皮卡丘,計(jì)算機(jī)專業(yè)學(xué)生,正在努力學(xué)習(xí)、努力敲代碼中! 讓我們一起繼續(xù)努力學(xué)習(xí)!
該文章參考學(xué)習(xí)教材為:
《Java EE企業(yè)級(jí)應(yīng)用開發(fā)教程 (Spring + Spring MVC +MyBatis)》 黑馬程序員 / 編著
文章以課本知識(shí)點(diǎn) + 代碼為主線,結(jié)合自己看書學(xué)習(xí)過程中的理解和感悟 ,最終成就了該文章文章用于本人學(xué)習(xí)使用 , 同時(shí)希望能幫助大家。
歡迎大家點(diǎn)贊?? 收藏? 關(guān)注??哦!??!(侵權(quán)教材方可聯(lián)系我,進(jìn)行刪除,如果雷同,純屬巧合)
1.Spring AOP簡介
1.1 AOP簡介
Spring的AOP模塊,是Spring框架體系結(jié)構(gòu)中十分重要的內(nèi)容,該模塊中提供了面向切面編程實(shí)現(xiàn)。
AOP的全稱是 Aspect-Oriented Programming,即面向切面編程( 也稱面向方面編程 )它是面向?qū)ο缶幊?OOP)的一種補(bǔ)充,目前已成為一種比較成熟的編程方式。
在傳統(tǒng)的業(yè)務(wù)處理代碼中,通常都會(huì)進(jìn)行事務(wù)處理、日志記錄等操作。雖然使用OOP可以通過組合或者繼承的方式來達(dá)到代碼的重用,但如果要實(shí)現(xiàn)某個(gè)功能(如日志記錄),同樣的代碼仍然會(huì)分散到各個(gè)方法中。這樣,如果想要關(guān)閉某個(gè)功能,或者對(duì)其進(jìn)行修改,就必須要修改所有的相關(guān)方法。這不但增加了開發(fā)人員的工作量,而且提高了代碼的出錯(cuò)率。(要實(shí)現(xiàn)AOP功能,核心關(guān)鍵之一是 : “代理”,通過“代理”可以獲得 “增強(qiáng)功能后的目標(biāo)對(duì)象” ,從而實(shí)現(xiàn)AOP功能)
為了解決這一問題,AOP思想隨之產(chǎn)生。AOP采取橫向抽取機(jī)制,將分散在各個(gè)方法中的重復(fù)代碼 提取出來,然后在程序編譯或運(yùn)行時(shí),再將這些提取出來的代碼應(yīng)用到需要執(zhí)行的地方。這種采用橫向抽取機(jī)制的方式,采用傳統(tǒng)的O0P思想顯然是無法辦到的,因?yàn)?0P只能實(shí)現(xiàn)父子關(guān)系的縱向的重用。雖然AOP是一種新的編程思想,但卻不是O0P的替代品,它只是OOP的延伸和補(bǔ)充。
在AOP思想中,類與切面的關(guān)系圖 如下所示 :
從上圖 (類與切面關(guān)系圖)可以看出,通過Aspect (切面) 分別在Class1和Class2的方法中加入了事務(wù)、日志、權(quán)限和異常等功能。
AOP的使用,使開發(fā)人員在編寫業(yè)務(wù)邏輯時(shí)可以專心于核心業(yè)務(wù),而不用過多地關(guān)注于其他業(yè)務(wù)邏輯的實(shí)現(xiàn),這不但提高了開發(fā)效率,而且增強(qiáng)了代碼的可維護(hù)性。
目前最流行的AOP框架有兩個(gè),分別為Spring AOP和 AspectJ。Spring AOP使用純Java實(shí)現(xiàn),不需要專門的編譯過程和類加載器,在運(yùn)行期間通過代理方式向目標(biāo)類織入增強(qiáng)的代碼。
AspectJ是一個(gè)基于Java語言的AOP框架,從Spring 2.0開始,Spring AOP引入了對(duì)AspectJ的支持,AspectJ擴(kuò)展了Java語言,提供了一個(gè)專門的編譯器,在編譯時(shí)提供橫向代碼的織入。
1.2 AOP術(shù)語
AOP術(shù)語包括 Aspect、Joinpoint、Pointcut、 Advice、 Target Object、Proxy 和 Weaving。具體講解如下。
一、Aspect ( 切面 ) :
在實(shí)際應(yīng)用中,切面通常是指封裝的用于橫向插入系統(tǒng)功能 (如事務(wù)、日志等) 的類。如 “類與切面關(guān)系圖” 的 Aspect。該 類要被Spring容器識(shí)別為切面,需要在配置文件<bean>元素指定 (哪個(gè)類將作為 切面)。
(切面 可以 理解為 “切面類”)二、Joinpoint ( 連接點(diǎn) ) :
在程序執(zhí)行過程中的某個(gè)階段點(diǎn),它實(shí)際上是對(duì)象的一個(gè)操作。
例子如:方法的調(diào)用或異常的拋出。在Spring AOP中,連接點(diǎn)就是指方法的調(diào)用 ( 非切面類的那個(gè)類的方法的調(diào)用)。三、Pointcut( 切入點(diǎn) ) :
是指切面與程序流程的 交叉點(diǎn),即那些 需要處理的連接點(diǎn) ( 即待處理的“ 方法的調(diào)用 ” )。
通常在程序中,切入點(diǎn)指的是類或者方法名。
例子如 :某個(gè)通知要應(yīng)用到所有以add開頭的方法中,那么所有滿足這一規(guī)則的方法都是切入點(diǎn)。“切面、連接點(diǎn) 和切入點(diǎn)” 關(guān)系圖如下圖所示” :
四、Advice( 通知 / 增強(qiáng)處理 ) :
AOP框架在特定的 切入點(diǎn) ( 類/方法名 )執(zhí)行的增強(qiáng)處理,即在定義好的切入點(diǎn)處所 要執(zhí)行的程序代碼。
可以將 ( Advice ) 理解為 切面中的方法,Advice 是 切面的具體實(shí)現(xiàn)。五、Target Object ( 目標(biāo)對(duì)象 ) :
是指所有 被通知的對(duì)象,也稱為被 增強(qiáng)對(duì)象。如果AOP框架采用的是動(dòng)態(tài)的AOP實(shí)現(xiàn),那么該對(duì)象就是一個(gè)被代理對(duì)象。六、Weaving ( 織入 ) :
將 切面類中代碼插入到目標(biāo)對(duì)象上,從而生成代理對(duì)象的過程。七、Proxy( 代理 ) :
將 通知應(yīng)用到目標(biāo)對(duì)象之后,被動(dòng)態(tài)創(chuàng)建的對(duì)象 ,該對(duì)象稱為“代理對(duì)象” / 增強(qiáng)功能后的目標(biāo)對(duì)象 ,
該對(duì)象可直接當(dāng)目標(biāo)對(duì)象使用。(Proxy (代理) 可理解為一個(gè) 過程,一個(gè)將目標(biāo)對(duì)象 進(jìn)行 增強(qiáng),變?yōu)?strong>代理對(duì)象 / “增強(qiáng)功能后的目標(biāo)對(duì)象的過程。) ( 代理就是AOP框架動(dòng)態(tài)生成的一個(gè)對(duì)象 )八、Proxy Class (代理類) :
實(shí)現(xiàn)“Proxy ( 代理 )功能”的類。該類 (代理類) 中包含了 Proxy(代理)”所需的功能代碼。九、Proxy Object (代理對(duì)象) / 增強(qiáng)功能后的目標(biāo)對(duì)象:
Proxy Object (代理對(duì)象) 稱為 / 可理解為 “增強(qiáng)功能后的目標(biāo)對(duì)象” 。該對(duì)象可直接當(dāng) “目標(biāo)對(duì)象” 使用。代理對(duì)象通常包含了目標(biāo)對(duì)象的功能,同時(shí)在 原目標(biāo)對(duì)象的基礎(chǔ)上 添加一些額外的功能或邏輯,例如日志記錄、事務(wù)管理等,因此可以說,代理對(duì)象可以被理解為"增強(qiáng)功能后的目標(biāo)對(duì)象“。
2.動(dòng)態(tài)代理
AOP中的==代理就是由AOP框架動(dòng)態(tài)生成的一個(gè)對(duì)象==,該對(duì)象可直接當(dāng)目標(biāo)對(duì)象使用。
(此處的代理 : 就是上面提到過的 生成 “增強(qiáng)功能后的目標(biāo)對(duì)象” 的代理)Spring 中的AOP代理,有 ①JDK動(dòng)態(tài)代理②CGLIB代理。(兩者都是 “動(dòng)態(tài)代理”)。
ps :① JDK動(dòng)態(tài)代理 和 CGlib代理都屬于 “動(dòng)態(tài)代理”。
② JDK動(dòng)態(tài)代理的目標(biāo)對(duì)象要實(shí)現(xiàn)一個(gè)或多個(gè)接口,如果要對(duì)沒有實(shí)現(xiàn)接口的目標(biāo)類 / 目標(biāo)對(duì)象 進(jìn)行代理時(shí),可用CGLIB代理。
2.1 JDK動(dòng)態(tài)代理
JDK動(dòng)態(tài)代理 是通過java.lang.reflect.Proxy類來實(shí)現(xiàn)的,我們可以調(diào)用Proxy類的 newProxyInstance() 方法來創(chuàng)建代理對(duì)象。對(duì)于使用業(yè)務(wù)接口的類,Sping 默認(rèn)會(huì)使用JDK代理來實(shí)現(xiàn)AOP。
(JDK動(dòng)態(tài)代理要實(shí)現(xiàn)接口,CGLIB代理不用實(shí)現(xiàn)接口。)
Spring 中JDK動(dòng)態(tài)代理的例子如 :
第一步、在DIEA中創(chuàng)建一個(gè)Java項(xiàng)目,添加web功能模塊。
第二步、在項(xiàng)目WEB-INF目錄中創(chuàng)建lib文件夾,存放Spring框架最基本核心的jar包,讓jar包生效。
獲取spring框架基本核心jar包
第三步、創(chuàng)建UserDao接口,在該接口中編寫添加 和 刪除的方法 。
創(chuàng)建UserDao的實(shí)現(xiàn)類 UserDaoImpl,分別實(shí)現(xiàn)接口中的方法,并在每個(gè)方法中添加一條輸出語句。
UserDao.javapublic interface UserDao{ public void addUser(); public void deleteUser(); }
UserDaoImpl.java
public class UserDaoImpl implements UserDao { // ”目標(biāo)類“,該類創(chuàng)建的對(duì)象為”目標(biāo)對(duì)象“ @Override public void addUser() { //這方法將要被“增強(qiáng)處理” System.out.println("添加用戶"); } @Override public void deleteUser() { //這方法將要被“增強(qiáng)處理” System.out.println("刪除用戶"); } }
實(shí)現(xiàn)類UserDaoImpl作為 目標(biāo)類 ,對(duì)其中的方法進(jìn)行 增強(qiáng)處理。
第四步、創(chuàng)建切面類 : MyAspect 。
在該類中定義一個(gè)模擬權(quán)限檢查的方法和一個(gè)模擬記錄日志的方法,這兩個(gè)方法就表示切面中的通知。public class MyAspect { //切面類 : 以下為兩個(gè)通知,將用于為“目標(biāo)對(duì)象”實(shí)現(xiàn)增強(qiáng)功能。 //通知 : 要執(zhí)行的切面類”的方法” //在特定的 “切入點(diǎn)” 進(jìn)行增強(qiáng)處理 : 在“切入點(diǎn)”的前或后,執(zhí)行切面類中的方法 public void check_Permissions() { //該方法即為“通知” : 切面類中的方法 System.out.println("模擬檢查權(quán)限..."); } public void log() { //通知 System.out.println("模擬記錄日志..."); } /* 對(duì)某個(gè)方法(切入點(diǎn))進(jìn)制“增強(qiáng)處理” : 即在程序執(zhí)行這個(gè)方法的“前或后”的時(shí)機(jī)執(zhí)行"切面方法"(添加通知) */ }
第五步、創(chuàng)建 代理類 JdkProxy ,該類實(shí)現(xiàn) InvocationHandler 接口,并編寫代理方法。在代理方法中,需要通過Proxy類實(shí)現(xiàn)動(dòng)態(tài)代理。
/** * JDK代理類 (JdkProxy) : 將通知應(yīng)用到目標(biāo)對(duì)象之后,被動(dòng)態(tài)創(chuàng)建的對(duì)象。 * 代理對(duì)象 : 經(jīng)過“代理”這一過程,生成的類即為“代理對(duì)象”,也稱為“增強(qiáng)功能后的目標(biāo)對(duì)象” * (可以理解為: “代理” / "代理類"的目的之一 : 生成“代理對(duì)象”) */ public class JdkProxy implements InvocationHandler { //聲明目標(biāo)類接口 private UserDao userDao; //創(chuàng)建代理方法 public Object createProxy(UserDao userDao) { this.userDao = userDao; //1.類加載器 (JdkProxy的類加載器) ---獲得JdkProxy對(duì)應(yīng)的“類加載器” ClassLoader classLoader = JdkProxy.class.getClassLoader(); //2.被代理對(duì)象實(shí)現(xiàn)的所有接口 (此處為: UserDao 的接口內(nèi)容等。) Class[] clazz = userDao.getClass().getInterfaces(); //3.使用代理類,進(jìn)行增強(qiáng),返回的是 ”代理后的對(duì)象“ / “增強(qiáng)功能后的目標(biāo)對(duì)象” return Proxy.newProxyInstance(classLoader, clazz, this); } /** * @param proxy 被代理后的對(duì)象 * @param method 將要執(zhí)行的方法信息 (反射) * @param args 執(zhí)行方法時(shí)需要的參數(shù) * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { /** 聲明切面類 (其中有要用在特定“切入點(diǎn)”的“通知”) 切入點(diǎn) : 要進(jìn)行“增強(qiáng)處理”/被“通知” 的 方法名 通知(也稱“增強(qiáng)處理”): 切面類中的方法 對(duì)切入點(diǎn)進(jìn)行“增強(qiáng)處理” : 在某個(gè)方法被執(zhí)行的前/后的時(shí)機(jī)執(zhí)行切面中的“方法” */ //創(chuàng)建切面類 MyAspect myAspect = new MyAspect(); /** * 目標(biāo)方法執(zhí)行的前后,會(huì)分別執(zhí)行切面類中的 check_Permissions()方法 和 log()方法 * 前增強(qiáng) : 執(zhí)行UserDao接口的方法之前先執(zhí)行 切面類中的 check_Permissions()這個(gè)方法 * 后增強(qiáng) : 執(zhí)行完接口的方法后執(zhí)行l(wèi)og()這個(gè)方法 */ //前增強(qiáng) myAspect.check_Permissions(); //在目標(biāo)類上調(diào)用方法,并傳入?yún)?shù) Object obj = method.invoke(userDao, args); //執(zhí)行UserDao接口中的方法 //后增強(qiáng) myAspect.log(); return obj; } }
上述例子中,JdkProxy類 (代理類) 實(shí)現(xiàn)了 InvocationHandler 接口,并實(shí)現(xiàn)了接口中的 invoke方法,所有動(dòng)態(tài)代理類所調(diào)用的方法都會(huì)交由該 ( invoke) 方法處理。在創(chuàng)建的代理方法 createProxy()中使用了 Proxy 類的 newProxyInstance()方法來創(chuàng)建 代理對(duì)象。newProxyInstance()方法中包含3個(gè)參數(shù),其中第1個(gè)參數(shù)是當(dāng)前類的類加載器,第2個(gè)參數(shù)表示的是被代理對(duì)象實(shí)現(xiàn)的所有接口,第3個(gè)參數(shù)this代表的就是代理類JdkProxy本身。在invoke()方法中,目標(biāo)類方法執(zhí)行的前后會(huì)分別執(zhí)行切面類中的check _Permissions() 方法和 log() 方法。
第六步、創(chuàng)建測試類 JdkTest,在該類中的main()方法中創(chuàng)建代理對(duì)象和目標(biāo)對(duì)象,然后從代理對(duì)象中獲得對(duì)目標(biāo)對(duì)象userDao增強(qiáng)功能后的對(duì)象,最后調(diào)用該對(duì)象的添加和刪除方法。
public class JdkTest { public static void main(String[] args) { //實(shí)例化JdkProxy類(JDK代理類) : 該類能產(chǎn)生“代理對(duì)象”(增強(qiáng)功能的目標(biāo)對(duì)象) JdkProxy jdkProxy = new JdkProxy(); //創(chuàng)建目標(biāo)對(duì)象 UserDao userDao = new UserDaoImpl(); /* 調(diào)用JdkProxy中的createProxy()方法獲得“代理對(duì)象” */ UserDao userDao1 = (UserDao)jdkProxy.createProxy(userDao); //指向UserDao中的兩個(gè)方法,該UserDao已被進(jìn)行AOP操作,其中方法執(zhí)行的前后都會(huì)執(zhí)行切面類中的方法 userDao1.addUser(); userDao1.deleteUser(); } }
測試類運(yùn)行結(jié)果 :
UserDao實(shí)例的添加用戶 和 刪除用戶的方法被成功調(diào)用,并且在調(diào)用前后分別添加了“權(quán)限檢查”和“記錄日志”的功能,這種實(shí)現(xiàn)了接口的代理方式,就是Spring中的JDK動(dòng)態(tài)代理。
2.2 CGLIB代理
JDK動(dòng)態(tài)代理的使用簡單,但它還有一定的局限性,使用 JDK動(dòng)態(tài)代理的對(duì)象必須實(shí)現(xiàn)一個(gè)或多個(gè)接口。
如果要對(duì)沒有實(shí)現(xiàn)接口的類進(jìn)行代理,那么可以使用 CGLIB代理。即目標(biāo)類 / 目標(biāo)對(duì)象,沒有實(shí)現(xiàn)任何接口時(shí),可用CGLIB代理。
ps:
JDK動(dòng)態(tài)代理的目標(biāo)對(duì)象要實(shí)現(xiàn)一個(gè)或多個(gè)接口,如果要對(duì)沒有實(shí)現(xiàn)接口的目標(biāo)類 / 目標(biāo)對(duì)象 進(jìn)行代理時(shí),可用CGLIB代理。CGLIB ( Code Generation Library )是一個(gè)高性能開源的代碼生成包,它采用非常底層的字節(jié)碼技術(shù),對(duì)指定的目標(biāo)類生成一個(gè)子類, 并對(duì)子類進(jìn)行增強(qiáng)。在Spring的核心包中已經(jīng)集成了CGLIB所需要的包,所以開發(fā)中不需要另外導(dǎo)入JAR包。
(JDK動(dòng)態(tài)代理 和 CGLIB代理 用核心包就行,不需要額外的JAR包)Spring 中JDK動(dòng)態(tài)代理的例子如 :
第一步、在DIEA中創(chuàng)建一個(gè)Java項(xiàng)目,添加web功能模塊。第二步、在項(xiàng)目WEB-INF目錄中創(chuàng)建lib文件夾,存放Spring框架最基本核心的jar包,讓jar包生效。
獲取spring框架基本核心jar包
第三步、創(chuàng)建目標(biāo)類 :UserDao,UserDao不需要實(shí)現(xiàn)任何接口,只需定義一個(gè)添加用戶的方法 和 一個(gè)刪除用戶的方法。
public class UserDao { //目標(biāo)類,沒實(shí)現(xiàn)任何接口,可用CGLIB代理 public void addUser() { System.out.println("添加用戶"); } public void deleteUser() { System.out.println("刪除用戶"); } }
第四步、創(chuàng)建切面類 : MyAspect 。
在該類中定義一個(gè)模擬權(quán)限檢查的方法和一個(gè)模擬記錄日志的方法,這兩個(gè)方法就表示切面中的通知。public class MyAspect { //切面類 : 以下為兩個(gè)通知,將用于為“目標(biāo)對(duì)象”實(shí)現(xiàn)增強(qiáng)功能。 //通知 : 要執(zhí)行的切面類”的方法” //在特定的 “切入點(diǎn)” 進(jìn)行增強(qiáng)處理 : 在“切入點(diǎn)”的前或后,執(zhí)行切面類中的方法 public void check_Permissions() { //該方法即為“通知” : 切面類中的方法 System.out.println("模擬檢查權(quán)限..."); } public void log() { //通知 System.out.println("模擬記錄日志..."); } /* 對(duì)某個(gè)方法(切入點(diǎn))進(jìn)制“增強(qiáng)處理” : 即在程序執(zhí)行這個(gè)方法的“前或后”的時(shí)機(jī)執(zhí)行"切面方法"(添加通知) */ }
第五步、創(chuàng)建代理類 : CglibProxy,該代理類需要實(shí)現(xiàn) MethodInterceptor 接口,并實(shí)現(xiàn)接口中的 intercept( ) 方法。
import com.myh.cglib.aspect.MyAspect; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; //代理類 public class CglibProxy implements MethodInterceptor { //CGLIB代理 : 目標(biāo)類不需要實(shí)現(xiàn)接口 //代理方法 public Object createProxy(Object target) { //創(chuàng)建“代理對(duì)象/增強(qiáng)后的目標(biāo)對(duì)象”的方法 //創(chuàng)建一個(gè)動(dòng)態(tài)類對(duì)象 (Enhancer : 增強(qiáng)器) Enhancer enhancer = new Enhancer(); //確定要增強(qiáng)的類,為Enhancer設(shè)置“父類” enhancer.setSuperclass(target.getClass()); //添加回調(diào)函數(shù) enhancer.setCallback(this); //返回創(chuàng)建的代理類 return enhancer.create(); } /** * @param proxy CGlib指定“父類”生成的“代理對(duì)象” * @param method 攔截的方法 * @param args 攔截方法的參數(shù)數(shù)組 * @param methodProxy 方法的代理對(duì)象,用于執(zhí)行父類的方法 * @throws Throwable */ @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { //創(chuàng)建切面類對(duì)象 MyAspect myAspect = new MyAspect(); //前增強(qiáng) myAspect.check_Permissions(); //目標(biāo)方法的執(zhí)行 Object obj = methodProxy.invokeSuper(proxy, args); //執(zhí)行"代理對(duì)象"的 “父類的方法” //后增強(qiáng) myAspect.log(); return obj; } }
在 CglibProxy類的代理方法中,首先創(chuàng)建了一個(gè)動(dòng)態(tài)類對(duì)象Enhancer,它是CGLIB的核心類;然后調(diào)用了Enhancer類的 setSuperclass()方法來確定目標(biāo)對(duì)象 (為動(dòng)態(tài)類對(duì)象指定父類 );接下來調(diào)用了setCallback()方法添加回調(diào)函數(shù),其中的this代表的就是代理類CglibProxy本身;最后通過return語句將創(chuàng)建的代理類對(duì)象返回。 intercept()方法會(huì)在程序執(zhí)行目標(biāo)方法時(shí)被調(diào)用,方法運(yùn)行時(shí)將會(huì)執(zhí)行切面類中的增強(qiáng)方法。
第六步、
創(chuàng)建測試類 CglibTest 在該類的main()方法中首先創(chuàng)建代理對(duì)象和目標(biāo)對(duì)象,然后從代理對(duì)象中獲得增強(qiáng)后的目標(biāo)對(duì)象,最后調(diào)用對(duì)象的添加和刪除方法。public class CglibTest { public static void main(String[] args) { //創(chuàng)建代理對(duì)象 CglibProxy cglibProxy = new CglibProxy(); //創(chuàng)建目標(biāo)對(duì)象 UserDao userDao = new UserDao(); UserDao userDao1 = (UserDao)cglibProxy.createProxy(userDao); //執(zhí)行方法 userDao1.addUser(); userDao1.deleteUser(); } }
測試類運(yùn)行結(jié)果 :
從測試類結(jié)果可以看出,目標(biāo)類UserDao中的方法被成功調(diào)用且增強(qiáng)了,這種 (目標(biāo)類) 沒有實(shí)現(xiàn)接口的代理方式,就是CGLIB代理。
3.基于“代理類”的AOP實(shí)現(xiàn)
- 動(dòng)態(tài)代理中的 JDK動(dòng)態(tài)代理 和 CGLIB代理都是基于“代理類”的AOP實(shí)現(xiàn)。
- Spring中的 AOP代理默認(rèn)使用 的是 JDK動(dòng)態(tài)代理。
- 在Spring中使用 ProxyFactoryBean 是創(chuàng)建 AOP代理 的最基本的方式。
3.1 Spring的通知類型
Spring 中的 通知 按照在 目標(biāo)類方法 的連接點(diǎn)位置,可以分為以下5種類型 :
- org.aopalliance.intercept.MethodInterceptor (環(huán)繞通知) :
在目標(biāo)方法執(zhí)行前后實(shí)施增強(qiáng),可以應(yīng)用于日志、事務(wù)管理等功能。- org.springframework. aop.MethodBeforeAdvice (前置通知) :
在目標(biāo)方法執(zhí)行前實(shí)施增強(qiáng),可以應(yīng)用于權(quán)限管理等功能。- org.springframework. aop.AfterReturningAdvice (后置通知) :
在目標(biāo)方法執(zhí)行后實(shí)施增強(qiáng),可以應(yīng)用于關(guān)閉流、上傳文件、 刪除臨時(shí)文件等功能- org.springframework.aop.ThrowsAdvice (異常通知) :
在方法拋出異常后實(shí)施增強(qiáng),可以應(yīng)用于處理異常記錄日志等功能。- org.springframework.aop.IntroductionInterceptor (引介通知) :
在目標(biāo)類中添加一些新的方法和屬性,可以應(yīng)用于修改老版本程序(增強(qiáng)類)。
3.2 ProxyFactoryBean ( 可通知.xml配置文件完成aop功能 )
ProxyFactoryBean是 FactoryBean接口的實(shí)現(xiàn)類,FactoryBean負(fù)責(zé)實(shí)例化一個(gè)Bean,
而 ProxyFactoryBean負(fù)責(zé)為其他Bean創(chuàng)建代理實(shí)例。在Spring中,使用ProxyFactoryBean 是 創(chuàng)建AOP代理的基本方式。ProxyFactoryBean類中的常用可配置屬性如下表所示 :
屬性名稱 描述 target 代理的目標(biāo)對(duì)象 proxyInterfaces 代理要實(shí)現(xiàn)的接口,如果是多個(gè)接口,可以使用**以下格式賦值
<list>
<value></value>
…
</list>proxyTargetClass 是否對(duì)類代理而不是接口,設(shè)置為true時(shí),使用CGLIB代理.
(設(shè)置為true表示是對(duì)類代理,自然是用 CGLIB代理)interceptorNames 需要織入目標(biāo)的Advice。 singleton 返回的代理是否為單實(shí)例,默認(rèn)為true (即返回單實(shí)例) optimize 當(dāng)設(shè)置為true時(shí),強(qiáng)制使用CGLIB 對(duì)ProxyFactoryBean類有了初步的了解后,接下來通過一個(gè)典型的環(huán)繞通知案例,來演示Spring使用ProxyFactoryBean創(chuàng)建AOP代理的過程,例子如下 :
第一步、
在核心JAR包的基礎(chǔ)上,項(xiàng)目的lib目錄中導(dǎo)入AOP的兩個(gè)JAR包,①spring-aop.jar 和 ②aopalliance.jar ;
spring-aop.jar : 是Spring為實(shí)現(xiàn)AOP功能提供的JAR包。
aopalliance.jar : 是AOP聯(lián)盟提供的規(guī)范包,是實(shí)現(xiàn)AOP功能所需JAR包。Spring核心jar + aop有關(guān)的jar
第二步、創(chuàng)建UserDao接口,在該接口中編寫添加 和 刪除的方法 。
創(chuàng)建UserDao的實(shí)現(xiàn)類 UserDaoImpl,分別實(shí)現(xiàn)接口中的方法,并在每個(gè)方法中添加一條輸出語句。
UserDao.javapublic interface UserDao{ public void addUser(); public void deleteUser(); }
UserDaoImpl.java
public class UserDaoImpl implements UserDao { // ”目標(biāo)類“,該類創(chuàng)建的對(duì)象為”目標(biāo)對(duì)象“ @Override public void addUser() { //這方法將要被“增強(qiáng)處理” System.out.println("添加用戶"); } @Override public void deleteUser() { //這方法將要被“增強(qiáng)處理” System.out.println("刪除用戶"); } }
實(shí)現(xiàn)類UserDaoImpl作為 目標(biāo)類 ,對(duì)其中的方法進(jìn)行 增強(qiáng)處理。
第三步、
創(chuàng)建切面類MyAspect。由于實(shí)現(xiàn)環(huán)繞通知需要實(shí)現(xiàn) org. aopalliance.intercept.MethodInterceptor接口,所以MyAspect類需要實(shí)現(xiàn)該接口,并實(shí)現(xiàn)接口中的 invoke() 方法,來執(zhí)行目標(biāo)方法。
MyAspect.javaimport org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; //切面類 public class MyAspect implements MethodInterceptor { //切面類,該切面類實(shí)現(xiàn)了aopalliance包下的 MethodInterceptor(環(huán)繞通知)接口 public void check_Permission() { //切面方法 (通知) System.out.println("模擬權(quán)限檢查..."); } public void log() { //切面方法 (通知) System.out.println("模擬記錄日志..."); } @Override public Object invoke(MethodInvocation mi) throws Throwable { /* 在目標(biāo)方法執(zhí)行的前后,分別執(zhí)行了“檢查權(quán)限” 和 “記錄日志”的方法,這兩個(gè)方法也就是“增強(qiáng)的方法”,也就是通知。 */ check_Permission(); //執(zhí)行目標(biāo)方法 Object obj = mi.proceed(); log(); return obj; } }
第四步、
創(chuàng)建配置文件applicationContext.xml,并指定對(duì)象。<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 1.目標(biāo)類 (把對(duì)象放入IOC容器中,作為Bean)--> <bean id="userDao" class="com.myh.factorybean.UserDaoImpl"/> <!-- 2.切面類 --> <bean id="myAspect" class="com.myh.factorybean.MyAspect"/> <!-- 3.使用Spring代理工廠定義一個(gè)名稱為: userDaoProxy 的代理對(duì)象 --> <bean id="userDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 指定“代理實(shí)現(xiàn)的接口” --> <property name="proxyInterfaces" value="com.myh.factorybean.UserDao"/> <!-- 指定“目標(biāo)對(duì)象” --> <property name="target" ref="userDao"/> <!-- 指定“切面”,植入環(huán)繞通知 --> <property name="interceptorNames" value="myAspect"/> <!-- 指定代理方式,true : 使用cglib, false(默認(rèn)) : 使用jdk動(dòng)態(tài)代理 --> <property name="proxyTargetClass" value="true"/> </bean> </beans>
上述配置文件代碼中,首先通過 <bean>元素定義了目標(biāo)類和切面,然后使用 ProxyFactoryBean類定義了代理對(duì)象。在定義的代理對(duì)象中,分別通過 <property>子元素指定了①代理實(shí)現(xiàn)的接口切代理、②目標(biāo)對(duì)象、③需要織入目標(biāo)類的通知以及④代理方式。
第五步、
創(chuàng)建ProxyFactoryBeanTest 測試類,在類中通過Spring容器獲取代理對(duì)象的實(shí)例,并執(zhí)行目標(biāo)方法。文章來源:http://www.zghlxwxcb.cn/news/detail-814823.html
package com.myh.factorybean; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class ProxyFactoryBeanTest { //測試類 public static void main(String[] args) { String xmlPath = "com/myh/factorybean/applicationContext.xml"; //創(chuàng)建ApplicationContext (從其中獲取IOC容器中的Bean) ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath); //從IOC容器中獲得“代理對(duì)象” UserDao userDao = (UserDao) applicationContext.getBean("userDaoProxy"); //獲得“增強(qiáng)功能后的目標(biāo)對(duì)象” //執(zhí)行目標(biāo)方法 userDao.addUser(); userDao.deleteUser(); } }
測試類運(yùn)行結(jié)果如下 :
文章來源地址http://www.zghlxwxcb.cn/news/detail-814823.html
到了這里,關(guān)于Spring | Srping AOP (AOP簡介、動(dòng)態(tài)代理、基于“代理類”的AOP實(shí)現(xiàn))的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!