寧波網(wǎng)站建設(shè)免費(fèi)咨詢(xún)漯河網(wǎng)絡(luò)推廣哪家好
?作者簡(jiǎn)介:大家好,我是Leo,熱愛(ài)Java后端開(kāi)發(fā)者,一個(gè)想要與大家共同進(jìn)步的男人😉😉
🍎個(gè)人主頁(yè):Leo的博客
💞當(dāng)前專(zhuān)欄: Spring專(zhuān)欄
?特色專(zhuān)欄: MySQL學(xué)習(xí)
🥭本文內(nèi)容:Spring5學(xué)習(xí)筆記—AOP編程
🖥?個(gè)人小站 :個(gè)人博客,歡迎大家訪問(wèn)
📚個(gè)人知識(shí)庫(kù): 知識(shí)庫(kù),歡迎大家訪問(wèn)
1. 靜態(tài)代理設(shè)計(jì)模式
1.1 為什么需要代理設(shè)計(jì)模式
-
在JavaEE分層開(kāi)發(fā)開(kāi)發(fā)中,那個(gè)層次對(duì)于我們來(lái)講最重要
DAO ---> Service --> Controller JavaEE分層開(kāi)發(fā)中,最為重要的是Service層
-
Service層中包含了哪些代碼?
Service層中 = 核心功能(幾十行 上百代碼) + 額外功能(附加功能) 1. 核心功能業(yè)務(wù)運(yùn)算DAO調(diào)用 2. 額外功能(事務(wù)、日志、性能...)1. 不屬于業(yè)務(wù)2. 可有可無(wú)3. 代碼量很小
-
額外功能書(shū)寫(xiě)在Service層中好不好?
Controller層(Service層的調(diào)用者)除了需要核心功能,還需要這些額外功能。
但是從軟件設(shè)計(jì)者角度看:Service層最好不要寫(xiě)額外功能。
-
現(xiàn)實(shí)生活中的解決方式
代理模式是一種比較好理解的設(shè)計(jì)模式
。簡(jiǎn)單來(lái)說(shuō)就是 我們使用代理對(duì)象來(lái)代替對(duì)真實(shí)對(duì)象(real object)的訪問(wèn),這樣就可以在不修改原目標(biāo)對(duì)象的前提下,提供額外的功能操作,擴(kuò)展目標(biāo)對(duì)象的功能。
代理模式的主要作用是擴(kuò)展目標(biāo)對(duì)象
的功能,比如說(shuō)在目標(biāo)對(duì)象的某個(gè)方法執(zhí)行前后你可以增加一些自定義的操作。
代理模式: 為一個(gè)對(duì)象提供一個(gè)替身,以控制對(duì)這個(gè)對(duì)象的訪問(wèn)。即通過(guò)代理對(duì)象訪問(wèn)目標(biāo)對(duì)象.這樣做的好處是:可以在目標(biāo)對(duì)象實(shí)現(xiàn)的基礎(chǔ)上,增強(qiáng)額外的功能操作,即擴(kuò)展目標(biāo)對(duì)象的功能。
被代理的對(duì)象可以是
遠(yuǎn)程對(duì)象
、創(chuàng)建開(kāi)銷(xiāo)大的對(duì)象或需要安全控制的對(duì)象
舉個(gè)例子:當(dāng)我們工作之后需要出去租房子,房東張貼廣告帶我看房子,最后簽合同,但是房東只想坐著簽合同并不想到處跑著看房子,于是就找了一個(gè)中介專(zhuān)門(mén)來(lái)宣傳廣告并且?guī)ё鈶?hù)看房子,而房東只負(fù)責(zé)簽合同收錢(qián)!中介在這里就可以看作是代理你的代理對(duì)象
,代理的行為(方法)
是帶租戶(hù)看房子。
1.2 代理設(shè)計(jì)模式分析
2.1 概念
通過(guò)代理類(lèi),為原始類(lèi)(目標(biāo))增加額外的功能 好處:利于原始類(lèi)(目標(biāo))的維護(hù)
2.2 名詞解釋
1. 目標(biāo)類(lèi) 原始類(lèi) 指的是 業(yè)務(wù)類(lèi) (核心功能 --> 業(yè)務(wù)運(yùn)算 DAO調(diào)用)
2. 目標(biāo)方法,原始方法目標(biāo)類(lèi)(原始類(lèi))中的方法 就是目標(biāo)方法(原始方法)
3. 額外功能 (附加功能)日志,事務(wù),性能
2.3 代理開(kāi)發(fā)的核心要素
代理類(lèi) = 實(shí)現(xiàn)和目標(biāo)類(lèi)相同的接口 + 在同名方法中添加額外功能 + 調(diào)用原始類(lèi)同名方法房東 ---> public interface UserService{m1m2}UserServiceImpl implements UserService{m1 ---> 業(yè)務(wù)運(yùn)算 DAO調(diào)用m2 }UserServiceProxy implements UserServicem1m2
2.4 編碼
靜態(tài)代理:為每一個(gè)原始類(lèi),手動(dòng)編寫(xiě)一個(gè)代理類(lèi) (.java .class)
2.5 靜態(tài)代理存在的問(wèn)題
1. 靜態(tài)類(lèi)文件數(shù)量過(guò)多,不利于項(xiàng)目管理UserServiceImpl UserServiceProxyOrderServiceImpl OrderServiceProxy
2. 額外功能維護(hù)性差代理類(lèi)中 額外功能修改復(fù)雜(麻煩)
2. Spring的動(dòng)態(tài)代理開(kāi)發(fā)
2.1 Spring動(dòng)態(tài)代理的概念
概念:通過(guò)
代理類(lèi)
為原始類(lèi)(目標(biāo)類(lèi))增加額外功能,代理類(lèi)由Spring動(dòng)態(tài)生成。
好處:利于原始類(lèi)
(目標(biāo)類(lèi))的維護(hù)
2.2 搭建開(kāi)發(fā)環(huán)境
<dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>5.1.14.RELEASE</version>
</dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjrt</artifactId><version>1.8.8</version>
</dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.3</version>
</dependency>
2.3 Spring動(dòng)態(tài)代理的開(kāi)發(fā)步驟
1. 創(chuàng)建原始對(duì)象(目標(biāo)對(duì)象)
public class UserServiceImpl implements UserService{@Overridepublic void register(User user) {System.out.println("UserServiceImpl.register");}@Overridepublic boolean login(String name, String password) {System.out.println("UserServiceImpl.login");return true;}
}
<bean id="userServiceImpl" class="com.Leo.dynamic.service.impl.UserServiceImpl"/>
2. 定義額外功能
實(shí)現(xiàn)MethodBeforeAdvice接口
public class Before implements MethodBeforeAdvice {//作用:給原始方法添加額外功能//注意:會(huì)在原始方法運(yùn)行之前運(yùn)行此方法@Overridepublic void before(Method method, Object[] args, Object target) throws Throwable {System.out.println("-----method before advice log------");}
}
<bean id="before" class="com.Leo.dynamic.service.Before"/>
3. 定義切入點(diǎn)
1. 切入點(diǎn):額外功能加入的位置2. 目的:由程序員根據(jù)自己的需要,決定額外功能加入給那個(gè)原始方法
register()
login()簡(jiǎn)單的測(cè)試:所有方法都做為切入點(diǎn),都加入額外的功能。
<aop:config><aop:pointcut id="pc" expression="execution(* *(..))"/>
</aop:config>
4. 組裝
<!-- 組裝切入點(diǎn)與額外功能 -->
<aop:advisor advice-ref="before" pointcut-ref="pc"/>
5. 測(cè)試調(diào)用
目的:獲得Spring工廠創(chuàng)建的動(dòng)態(tài)代理對(duì)象,并進(jìn)行調(diào)用
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
注意:1. Spring的工廠通過(guò) 原始對(duì)象的id值獲得的是代理對(duì)象2. 獲得代理對(duì)象后,可以通過(guò)聲明接口類(lèi)型,進(jìn)行對(duì)象的存儲(chǔ)UserService userService=(UserService)ctx.getBean("userServiceImpl");userService.login("","");
userService.register(new User());
控制臺(tái)打印: 可以發(fā)現(xiàn)在日志之前輸入了
2.4 動(dòng)態(tài)代理細(xì)節(jié)分析
4.1 Spring創(chuàng)建的動(dòng)態(tài)代理類(lèi)在哪里?
Spring框架在運(yùn)行時(shí),通過(guò)動(dòng)態(tài)字節(jié)碼技術(shù),在JVM創(chuàng)建的,運(yùn)行在JVM內(nèi)部,等程序結(jié)束后就消失了。
什么叫動(dòng)態(tài)字節(jié)碼技術(shù):通過(guò)第三方動(dòng)態(tài)字節(jié)碼框架,在JVM中創(chuàng)建對(duì)應(yīng)類(lèi)的字節(jié)碼,進(jìn)而創(chuàng)建對(duì)象,當(dāng)虛擬機(jī)結(jié)束,動(dòng)態(tài)字節(jié)碼跟著消失。
結(jié)論:動(dòng)態(tài)代理不需要定義類(lèi)文件,都是JVM運(yùn)行過(guò)程中動(dòng)態(tài)創(chuàng)建的,所以不會(huì)造成靜態(tài)代理,類(lèi)文件數(shù)量過(guò)多,影響項(xiàng)目管理的問(wèn)題。
4.2 動(dòng)態(tài)代理編程簡(jiǎn)化代理的開(kāi)發(fā)
在額外功能不改變的前提下,創(chuàng)建其他目標(biāo)類(lèi)(原始類(lèi))的代理對(duì)象時(shí),只需要指定原始(目標(biāo))對(duì)象即可。
3. Spring動(dòng)態(tài)代理詳解
3.1 額外功能的詳解
-
MethodBeforeAdvice分析
作用:
原始方法執(zhí)行之前,運(yùn)行額外功能。
public class Before implements MethodBeforeAdvice {/*** 作用:給原始方法添加額外功能* 注意:會(huì)在原始方法運(yùn)行之前運(yùn)行此方法* @param method 原始方法 login() register() ...* @param objects 原始方法的參數(shù)列表 name password ...* @param o 原始對(duì)象 UserServiceImpl OrderServiceImpl* @throws Throwable 拋出的異常*/@Overridepublic void before(Method method, Object[] objects, Object o) throws Throwable {System.out.println("---- MethodBeforeAdvice log... ----");} }
實(shí)戰(zhàn):需要時(shí)才用,可能都會(huì)用到,也有可能都不用。
-
MethodInterceptor(方法攔截器)
MethodInterceptor
接口:額外功能可定義在原始方法執(zhí)行 前、后、前和后。public class Around implements MethodInterceptor {/*** @param invocation 封裝了原始方法 invocation.proceed()表示原始方法的運(yùn)行* @return 原始方法的返回值* @throws Throwable 可能拋出的異常*/@Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {System.out.println("------ 額外功能 log -----");//原始方法的執(zhí)行Object ret = invocation.proceed();//返回原始方法的返回值return ret;} }
額外功能運(yùn)行在原始方法執(zhí)行之后
public Object invoke(MethodInvocation invocation) throws Throwable {Object ret = invocation.proceed();System.out.println("-----額外功能運(yùn)行在原始方法執(zhí)行之后----");return ret;}
額外功能運(yùn)行在原始方法執(zhí)行之前和之后(實(shí)戰(zhàn):事務(wù)需要在之前和之后都運(yùn)行)
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {System.out.println("-----額外功能運(yùn)行在原始方法執(zhí)行之前----");Object ret = invocation.proceed();System.out.println("-----額外功能運(yùn)行在原始方法執(zhí)行之后----");return ret;
}
額外功能運(yùn)行在原始方法拋出異常時(shí)
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {Object ret = null;try {ret = invocation.proceed();} catch (Throwable throwable) {System.out.println("-----原始方法拋出異常 執(zhí)行的額外功能 ---- ");throwable.printStackTrace();}return ret;}
MethodInterceptor可以影響原始方法的返回值
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {System.out.println("------log-----");Object ret = invocation.proceed();//拿到原始方法的返回值后進(jìn)行一些操作就會(huì)影響,直接返回就不影響return false;
}
3.2 切入點(diǎn)詳解
切入點(diǎn)決定了額外功能加入的位置。
<aop:pointcut id="pc" expression="execution(* *(..))"/>
exection(* *(..)) ---> 匹配了所有方法 a b c 1. execution() 切入點(diǎn)函數(shù)
2. * *(..) 切入點(diǎn)表達(dá)式
1. 切入點(diǎn)表達(dá)式
-
方法切入點(diǎn)表達(dá)式:
* *(..) --> 所有方法* ---> 修飾符 返回值 * ---> 方法名 ()---> 參數(shù)表 ..---> 對(duì)于參數(shù)沒(méi)有要求 (0個(gè)或多個(gè))
舉例:
# 定義login方法作為切入點(diǎn)* login(..)# 定義register作為切入點(diǎn)* register(..)# 定義名字為login且有兩個(gè)字符串類(lèi)型參數(shù)的方法 作為切入點(diǎn)* login(String,String)# 注意:非java.lang包中的類(lèi)型,必須要寫(xiě)全限定名* register(com.Leo.proxy.User)# ..可以和具體的參數(shù)類(lèi)型連用(至少有一個(gè)參數(shù)是String類(lèi)型)* login(String,..)# 精準(zhǔn)方法切入點(diǎn)限定# 修飾符 返回值 包.類(lèi).方法(參數(shù))* com.yuziayn.proxy.UserServiceImpl.login(..)* com.Leo.proxy.UserServiceImpl.login(String,String)
-
類(lèi)切入點(diǎn)表達(dá)式:
指定特定的類(lèi)作為切入點(diǎn),即這個(gè)類(lèi)中所有的方法都會(huì)加上額外功能。
舉例:
# 類(lèi)中的所有方法都加入額外功能 * com.Leo.proxy.UserServiceImpl.*(..)# 忽略包 # 1. 類(lèi)只在一級(jí)包下 com.UserServiceImpl * *.UserServiceImpl.*(..)# 2. 類(lèi)可在多級(jí)包下 com.Leo.proxy.UserServiceImpl * *..UserServiceImpl.*(..)
-
包切入點(diǎn)表達(dá)式:
指定包作為切入點(diǎn),即這個(gè)包中的所有類(lèi)及其方法都會(huì)加入額外的功能。
舉例:
# proxy包作為切入點(diǎn),即proxy包下所有類(lèi)中的所有方法都會(huì)加入額外功能,但是不包括其子包中的類(lèi)! * com.Leo.proxy.*.*(..)# 當(dāng)前包及其子包都生效 * com.Leo.proxy..*.*(..)
2 切入點(diǎn)函數(shù)
作用:用于執(zhí)行切入點(diǎn)表達(dá)式。
-
execution()
最為重要的切入點(diǎn)函數(shù),功能最全! 用于執(zhí)行:方法切入點(diǎn)表達(dá)式、類(lèi)切入點(diǎn)表達(dá)式、包切入點(diǎn)表達(dá)式 弊端:execution執(zhí)行切入點(diǎn)表達(dá)式 ,書(shū)寫(xiě)麻煩execution(* com.Leo.proxy..*.*(..))注意:其他的切入點(diǎn)函數(shù) 只是簡(jiǎn)化execution書(shū)寫(xiě)復(fù)雜度,功能上完全一致
-
args()
# 作用:用于函數(shù)(方法)參數(shù)的匹配# 舉例:方法參數(shù)必須得是2個(gè)字符串類(lèi)型的參數(shù)execution(* *(String,String))等價(jià)于:args(String,String)
-
within()
# 作用:用于進(jìn)行類(lèi)、包切入點(diǎn)表達(dá)式的匹配 # 舉例: # UserServiceImpl類(lèi)作為切入點(diǎn):execution(* *..UserServiceImpl.*(..))within(*..UserServiceImpl) # proxy包作為切入點(diǎn):execution(* com.Leo.proxy..*.*(..))within(com.yuziayan.proxy..*)
-
@annotation()
<!-- 作用:為具有特殊注解的方法加入額外功能 --><aop:pointcut id="" expression="@annotation(com.baizhiedu.Log)"/>
-
切入點(diǎn)函數(shù)間的邏輯運(yùn)算:
目的:整合多個(gè)切入點(diǎn)函數(shù)一起配合工作,進(jìn)而完成更為復(fù)雜的需求。
-
and 與操作(同時(shí)滿(mǎn)足)
# 案例:方法名為login,同時(shí)有2個(gè)字符串類(lèi)型的參數(shù):execution(* login(String,String))execution(* login(..)) and args(String,String)# 注意:與操作不能用于同種類(lèi)型的切入點(diǎn)函數(shù) # 錯(cuò)誤案例:register方法 和 login方法作為切入點(diǎn)(不能用and,而用or!)execution(* login(..)) and execution(* register(..)) # 上面的語(yǔ)句會(huì)發(fā)生錯(cuò)誤,因?yàn)槠鋵?shí)際表達(dá)的含義是方法名為login同時(shí)方法名為register,顯然有悖邏輯,此時(shí)應(yīng)該用到的是 or
-
-
or 或操作(滿(mǎn)足一種即可)
# 案例:register方法 和 login方法作為切入點(diǎn) execution(* login(..)) or execution(* register(..))
4. AOP編程
4.1 AOP概念
# AOP (Aspect Oriented Programing) 面向切面編程 = Spring動(dòng)態(tài)代理開(kāi)發(fā)
# 以切面為基本單位的程序開(kāi)發(fā),通過(guò)切面間的彼此協(xié)同,相互調(diào)用,完成程序的構(gòu)建
# 切面 = 切入點(diǎn) + 額外功能# OOP (Object Oriented Programing) 面向?qū)ο缶幊?Java
# 以對(duì)象為基本單位的程序開(kāi)發(fā),通過(guò)對(duì)象間的彼此協(xié)同,相互調(diào)用,完成程序的構(gòu)建# POP (Procedure Oriented Programing) 面向過(guò)程(方法、函數(shù))編程 C
# 以過(guò)程為基本單位的程序開(kāi)發(fā),通過(guò)過(guò)程間的彼此協(xié)同,相互調(diào)用,完成程序的構(gòu)建# AOP的概念:本質(zhì)就是Spring的動(dòng)態(tài)代理開(kāi)發(fā),通過(guò)代理類(lèi)為原始類(lèi)增加額外功能。好處:利于原始類(lèi)的維護(hù)
# 注意:AOP編程不可能取代OOP,而是OOP編程的補(bǔ)充。
4.2 AOP編程的開(kāi)發(fā)步驟
- 原始對(duì)象
- 額外功能 (MethodInterceptor)
- 切入點(diǎn)
- 組裝切面 (額外功能+切入點(diǎn))
4.3 切面的名詞解釋
切面 = 切入點(diǎn) + 額外功能 幾何學(xué)面 = 點(diǎn) + 相同的性質(zhì)
5. AOP的底層實(shí)現(xiàn)原理
5.1 核心問(wèn)題
- AOP如何創(chuàng)建動(dòng)態(tài)代理類(lèi)?(動(dòng)態(tài)字節(jié)碼技術(shù))
- Spring工廠如何加工創(chuàng)建代理對(duì)象?通過(guò)原始對(duì)象的id值,獲得的是代理對(duì)象。
5.2 動(dòng)態(tài)代理類(lèi)的創(chuàng)建
1. JDK的動(dòng)態(tài)代理
-
Proxy.newProxyInstance()
方法參數(shù)詳解:
-
編碼:
public class TestJDKProxy {public static void main(String[] args) {//1.創(chuàng)建原始對(duì)象//注意:由于后面匿名子類(lèi)的方法中用到了userService,所以應(yīng)該用final修飾// 而JDK1.8以后默認(rèn)加了final,不需要手動(dòng)加UserService userService = new UserServiceImpl();//2.JDK創(chuàng)建代理對(duì)象InvocationHandler handler = new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("----------- JDKProxy log -----------");\//目標(biāo)方法運(yùn)行:Object ret = method.invoke(userService, args);return ret;}};UserService userServiceProxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(),handler);userServiceProxy.login("Leo", "123456");userServiceProxy.register(new User());} }
2. CGlib的動(dòng)態(tài)代理
- 原理:通過(guò)父子繼承關(guān)系創(chuàng)建代理對(duì)象。原始類(lèi)作為父類(lèi),代理類(lèi)作為子類(lèi),這樣既可以保證2者方法一致,同時(shí)在代理類(lèi)中提供新的實(shí)現(xiàn)(額外功能+原始方法)
-
CGlib編碼:
package com.Leo.cglib;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 TestCGlibProxy {public static void main(String[] args) {//1.創(chuàng)建原始對(duì)象UserServiceImpl userService = new UserServiceImpl();//2.通過(guò)CGlib創(chuàng)建代理對(duì)象// 2.1 創(chuàng)建EnhancerEnhancer enhancer = new Enhancer();// 2.2 設(shè)置借用類(lèi)加載器enhancer.setClassLoader(TestCGlibProxy.class.getClassLoader());// 2.3 設(shè)置父類(lèi)(目標(biāo)類(lèi))enhancer.setSuperclass(userService.getClass());// 2.4 設(shè)置回調(diào),額外功能寫(xiě)在里面enhancer.setCallback(new MethodInterceptor() {//相當(dāng)于 InvocationHandler --> invoke()@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {//額外功能:System.out.println("========= CGlibProxy log ========");//目標(biāo)方法執(zhí)行:Object ret = method.invoke(userService, objects);return ret;}});// 2.5 通過(guò)Enhancer對(duì)象創(chuàng)建代理UserServiceImpl service = (UserServiceImpl) enhancer.create();//測(cè)試:service.register();service.login();} }
3. 總結(jié)
1. JDK動(dòng)態(tài)代理 Proxy.newProxyInstance()
# 通過(guò)目標(biāo)類(lèi)實(shí)現(xiàn)的接口創(chuàng)建代理類(lèi)
2. Cglib動(dòng)態(tài)代理 Enhancer
# 通過(guò)繼承目標(biāo)類(lèi)創(chuàng)建代理類(lèi)
5.3 Spring工廠如何返回代理對(duì)象
- 思路分析:
-
編碼模擬:
public class ProxyBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {InvocationHandler invocation = new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("----------- 模擬Spring返回代理對(duì)象的方式 log -----------");Object ret = method.invoke(bean, args);return ret;}};return Proxy.newProxyInstance(ProxyBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), invocation);} }
<!-- 1.配置原始對(duì)象 --><bean id="userService" class="com.Leo.factory.UserServiceImpl"></bean><!-- 2.配置自己模擬的ProxyBeanPostProcessor --><bean id="proxyBeanPostProcessor" class="com.Leo.factory.ProxyBeanPostProcessor"/>
6. 基于注解的AOP編程
6.1 開(kāi)發(fā)步驟:
-
原始對(duì)象
-
額外功能
-
切入點(diǎn)
-
組裝切面
/*** 聲明切面類(lèi) @Aspect* 定義額外功能 @Around* 定義切入點(diǎn) @Around("execution(* login(..))")**/ @Aspect public class MyAspect {@Around("execution(* login(..))")//組裝了切入點(diǎn)和額外功能public Object around(ProceedingJoinPoint joinPoint) throws Throwable {//額外功能:System.out.println("--------- 基于注解的AOP編程 log --------");//原始方法執(zhí)行:Object ret = joinPoint.proceed();return ret;} }
<!-- 原始對(duì)象 --><bean id="userService" class="com.Leo.aspect.UserServiceImpl"></bean><!-- 切面 --><bean id="myAspect" class="com.Leo.aspect.MyAspect"/><!-- 開(kāi)啟基于注解的AOP編程 --><aop:aspectj-autoproxy/>
6.2 細(xì)節(jié)分析:
-
切入點(diǎn)復(fù)用:
@Aspect public class MyAspect {/*** 切入點(diǎn)復(fù)用:定義一個(gè)函數(shù),加上@Pointcut注解,通過(guò)該注解的value定義切入點(diǎn)表達(dá)式,以后可以復(fù)用。*/@Pointcut("execution(* login(..))")public void myPointcut(){}@Around("myPointcut()")//組裝了切入點(diǎn)和額外功能public Object around(ProceedingJoinPoint joinPoint) throws Throwable {//額外功能:System.out.println("--------- 基于注解的AOP編程 log --------");//原始方法執(zhí)行:Object ret = joinPoint.proceed();return ret;}@Around("myPointcut()")public Object around1(ProceedingJoinPoint joinPoint) throws Throwable {//額外功能:System.out.println("--------- 基于注解的AOP編程 tx --------");//原始方法執(zhí)行:Object ret = joinPoint.proceed();return ret;}}
-
動(dòng)態(tài)代理的創(chuàng)建方式:
AOP底層實(shí)現(xiàn) 2種代理創(chuàng)建方式1. JDK 通過(guò)實(shí)現(xiàn)接口,創(chuàng)建代理對(duì)象2. Cglib 通過(guò)繼承目標(biāo)類(lèi),創(chuàng)建代理對(duì)象默認(rèn)情況 AOP編程 底層應(yīng)用JDK動(dòng)態(tài)代理創(chuàng)建方式 如果要切換Cglib1. 基于注解AOP開(kāi)發(fā)<aop:aspectj-autoproxy proxy-target-class="true" />2. 傳統(tǒng)的AOP開(kāi)發(fā)<aop:config proxy-target-class="true"></aop>
7. AOP開(kāi)發(fā)中的一個(gè)坑
坑:在同一個(gè)業(yè)務(wù)類(lèi)中,業(yè)務(wù)方法間相互調(diào)用時(shí),只有最外層的方法,加入了額外功能(內(nèi)部的方法,通過(guò)普通的方式調(diào)用,運(yùn)行的都是原始方法)。如果想讓內(nèi)層的方法也調(diào)用代理對(duì)象的方法,就要實(shí)現(xiàn)AppicationContextAware獲得工廠,進(jìn)而獲得代理對(duì)象。
public class UserServiceImpl implements UserService, ApplicationContextAware {private ApplicationContext ctx;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.ctx = applicationContext;}@Log@Overridepublic void register(User user) {System.out.println("UserServiceImpl.register 業(yè)務(wù)運(yùn)算 + DAO ");//throw new RuntimeException("測(cè)試異常");//調(diào)用的是原始對(duì)象的login方法 ---> 核心功能/*設(shè)計(jì)目的:代理對(duì)象的login方法 ---> 額外功能+核心功能ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext2.xml");UserService userService = (UserService) ctx.getBean("userService");userService.login();Spring工廠重量級(jí)資源 一個(gè)應(yīng)用中 應(yīng)該只創(chuàng)建一個(gè)工廠*/UserService userService = (UserService) ctx.getBean("userService");userService.login("Leo", "123456");}@Overridepublic boolean login(String name, String password) {System.out.println("UserServiceImpl.login");return true;}
}