3.1 增加功能,導(dǎo)致的一些問題
1皱碘、源代碼中、業(yè)務(wù)方法中增加的功能
1)源代碼改動(dòng)的比較多
2)重復(fù)代碼比較多
3)代碼難以維護(hù)
3.2 AOP概念
3.2.1 什么是AOP
AOP(Aspect Orient Programming):面向切面編程
- Aspect:表示切面,給業(yè)務(wù)方法增加功能斋陪,叫做切面。切面一般都是非業(yè)務(wù)功能置吓,而且切面功能一般都是可以復(fù)用的无虚。例如:日志、事務(wù)衍锚、權(quán)限檢查友题、參數(shù)檢查、統(tǒng)計(jì)信息等
- Orient:面向戴质、對(duì)著
- Programming:編程
怎么理解面向切面編程度宦?(以切面為核心,設(shè)計(jì)開發(fā)你的應(yīng)用) - 設(shè)計(jì)項(xiàng)目的時(shí)候告匠,找出切面的功能戈抄。
- 安排切面的執(zhí)行時(shí)間、位置
3.2.2 AOP的作用
1)讓切面功能復(fù)用
2)讓開發(fā)人員專注于業(yè)務(wù)邏輯后专、提高開發(fā)效率
3)實(shí)現(xiàn)業(yè)務(wù)功能和非業(yè)務(wù)功能解耦合
4)給存在的業(yè)務(wù)方法增加功能划鸽,不用修改原來的代碼
3.2.3 AOP中的一些術(shù)語
1) Aspect:切面,給業(yè)務(wù)方法增加的功能
2)JoinPoint:連接點(diǎn)行贪,連接切面的業(yè)務(wù)方法漾稀,在這個(gè)業(yè)務(wù)方法執(zhí)行時(shí)模闲,同時(shí)執(zhí)行這個(gè)切面的功能
3)PointCut:是一個(gè)或多個(gè)連接點(diǎn)的集合,表示這些方法執(zhí)行時(shí)崭捍,都能去增加切面的功能尸折。表示切面執(zhí)行的位置
4)target:目標(biāo)對(duì)象,給哪個(gè)對(duì)象增加切面方法殷蛇,這個(gè)對(duì)象就是目標(biāo)對(duì)象
5)Advice:通知(IT術(shù)語:增強(qiáng))实夹,表示切面的執(zhí)行時(shí)間,在目標(biāo)方法之前執(zhí)行切面粒梦,還是在目標(biāo)方法之后執(zhí)行亮航。
AOP中三個(gè)重要的要素:
Aspect、PointCut匀们、Advice缴淋。這個(gè)概念的理解是:在Advice的時(shí)間,在PointCut的位置執(zhí)行Aspect
AOP是一個(gè)動(dòng)態(tài)的思想泄朴,在程序執(zhí)行期間重抖,創(chuàng)建代理(ServiceProxy),使用代理執(zhí)行方法時(shí)祖灰,增加切面的功能钟沛,這個(gè)代理對(duì)象時(shí)存在內(nèi)存中的
3.2.4 什么時(shí)候用AOP
你要給某些方法,增加一些相同的功能局扶,源代碼不能改恨统,給業(yè)務(wù)方法增加非業(yè)務(wù)功能。
3.2.5 AOP技術(shù)思想的實(shí)現(xiàn)
使用框架實(shí)現(xiàn)AOP三妈,實(shí)現(xiàn)AOP的框架很多畜埋,比較著名的有兩個(gè):
1)Spring:Spring框架實(shí)現(xiàn)AOP思想中的部分功能,Spring框架實(shí)現(xiàn)AOP的操作比較繁瑣沈跨、比較重
2)AspectJ:一個(gè)獨(dú)立的框架由捎,專門做AOP的。屬于Eclipse基金會(huì)的
3.3 使用AspectJ框架實(shí)現(xiàn)AOP
Aspectj框架可以使用注解和xml配置文件兩種方式實(shí)現(xiàn)AOP
3.3.1 通知
Aspectj表示起么的執(zhí)行時(shí)間饿凛,用的通知(Advice)狞玛。這個(gè)通知可以用注解表示.
講5個(gè)注解,表示切面的5個(gè)執(zhí)行時(shí)間涧窒,這些注解叫做通知注解
@Before:前置通知
@AfterReturning:后置通知
@Aroud:環(huán)繞通知
@AfterThrowing:異常通知
@After:最終通知
3.3.2 PointCut 位置
PointCut用來表示切面執(zhí)行的位置心肪,使用AspectJ中切入點(diǎn)表達(dá)式
切入點(diǎn)表達(dá)式語法:execution(訪問權(quán)限 方法的返回值 方法聲明(參數(shù)) 異常類型)
3.3.3 @Before 前置通知
/**
* 切面類中的通知方法,是可以有參數(shù)的
* JointPoint必須是他
* JointPoint:表示正在執(zhí)行的業(yè)務(wù)方法纠吴,相當(dāng)于反射中的Method
* 使用要求:必須是參數(shù)列表中的第一個(gè)
* 作用:獲取方法執(zhí)行時(shí)的信息硬鞍,例如:方法的名稱、參數(shù)集合(如果想要在切面類中,獲取方法的信息固该,則需要加這個(gè))
*/
@Before(value = "execution(* *..SomeServiceImpl.do*(..) )" )
public void myBefore2(JoinPoint joinPoint){
//獲取方法的定義
System.out.println("前置通知中锅减,獲取目標(biāo)方法的定義:"+joinPoint);
System.out.println("前置通知中,獲取目標(biāo)方法的名稱:"+joinPoint.getSignature().getName());
Object args[] = joinPoint.getArgs();
for (Object arg : args) {
System.out.println("獲取方法執(zhí)行時(shí)候的參數(shù):"+arg);
}
String methodName = joinPoint.getSignature().getName();
if ("doSome".equals(methodName)){
System.out.println("doSome=========");
}else if ("doOther".equals(methodName)){
System.out.println("doOther========");
}
}
3.3.4 后置通知@AfterReturning
@AfterReturning:在 目標(biāo)方法之后執(zhí)行
/**
* 后置通知方法的定義:
* 1)方法是public的
* 2)方法是void
* 3)方法名稱自定義
* 4)方法有參數(shù)伐坏,推薦使用object類型
*/
/**
* @AfterReturning:后置通知
* 屬性:
* value:切入點(diǎn)表達(dá)式
* returning:自定義的變量怔匣,表示目標(biāo)方法的返回值
* 自定義變量名稱必須和通知方法的形參名一樣
* 特點(diǎn):
* 1、在目標(biāo)方法之后執(zhí)行
* 2桦沉、能獲取到目標(biāo)方法的執(zhí)行結(jié)果
* 3每瞒、不會(huì)影響目標(biāo)方法的執(zhí)行
* 方法的參數(shù):
* Object res:表示目標(biāo)方法的返回值,使用res接收方法的調(diào)用結(jié)果
*
* 后置通知的執(zhí)行順序
* Object res = SomeServiceImpl.doOther(..);
* myAfterReturning(res)
*
* 方法返回的是String纯露、Integer剿骨、Long等基本類型,在后置通知中埠褪,修改返回浓利,是不會(huì)影響目標(biāo)方法的最后調(diào)用結(jié)果的
* 如果返回的是對(duì)象類型,例如:Student,在后置方法中钞速,修改這個(gè)Student對(duì)象的屬性值荞膘,會(huì)影響最后的調(diào)用結(jié)果
*/
@AfterReturning(value = "execution(* *..SomeServiceImpl.do*(..))",returning = "res")
public void myAfterReturning(JoinPoint jp,Object res){
System.out.println("后置通知,在目標(biāo)方法之后執(zhí)行的玉工,能拿到執(zhí)行結(jié)果:"+res);
//Object res有什么用,可以根據(jù)返回值得不同淘菩,做不同的增強(qiáng)功能
if (null != res){
res = "Hello";
}
System.out.println("后置通知遵班,在目標(biāo)方法之后執(zhí)行的,拿到執(zhí)行的結(jié)果:"+res);
}
3.3.5 @Around環(huán)繞通知
@Around(value="切入點(diǎn)表達(dá)式")
使用環(huán)繞通知時(shí)潮改,就是調(diào)用我們切面類中的通知方法
package com.wh.ch09aspectaround.handle;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import java.util.Date;
/**
* @Author :wanghui
* @Date 2022/1/2 - 9:35
*/
@Aspect
public class MyAspect {
/**
* 煥榮通知設(shè)置:
* 1狭郑、方法是public
* 2、方法必須有返回值汇在,推薦使用Object類型
* 3翰萨、方法名稱自定義
* 4、方法必須有ProceedingJoinPoint參數(shù)
*/
/**
* @Around:環(huán)繞通知
* 屬性:
* value:切入點(diǎn)表達(dá)式
* 位置:方法定義的上面
*返回值:Object:表示調(diào)用目標(biāo)方法糕殉,期望的執(zhí)行結(jié)果亩鬼,但是這個(gè)結(jié)果不一定是目標(biāo)方法自己的返回值
*參數(shù):ProceedingJoinPoint,相當(dāng)于反射中的method
* 作用:執(zhí)行目標(biāo)方法的,等于Method.invoke()
* 特點(diǎn):
* 1阿蝶、在目標(biāo)方法的前和后雳锋,都能增強(qiáng)功能
* 2、控制目標(biāo)方法是否執(zhí)行
* 3羡洁、可以修改目標(biāo)方法的執(zhí)行結(jié)果
*
*/
@Around("execution(* *..SomeServiceImpl.doFirst(..))")
public Object myAroud(ProceedingJoinPoint pjp) throws Throwable {
//執(zhí)行目標(biāo)方法
Object methodReturn = null;
System.out.println("執(zhí)行了環(huán)繞通知的MyAround方法,在目標(biāo)方法之前玷过,輸出日志時(shí)間==="+new Date());
methodReturn = pjp.proceed();//method invoke,表示執(zhí)行doFirst()方法本身
System.out.println("環(huán)繞通知,在目標(biāo)方法之后,輸出日志時(shí)間:"+new Date());
//返回目標(biāo)方法的執(zhí)行結(jié)果
return methodReturn;
}
}
能否說一個(gè)前置通知+一個(gè)后置通知就等于一個(gè)環(huán)繞呢辛蚊?
答案是不可以的
環(huán)繞可以控制方法的返回值粤蝎,而前置、后置沒法袋马〕跖欤可以模擬在方法前后加返回值。
3.3.6 @AfterThrowing異常通知
語法:@AafterThrowing(value="切入點(diǎn)表達(dá)式",throwing="自定義變量")
package com.wh.ch09aspectaround.handle;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import java.util.Date;
/**
* @Author :wanghui
* @Date 2022/1/2 - 9:35
*/
@Aspect
public class MyAspect{
/**
* 異常通知方法的定義:
* 1飞蛹、方法是public
* 2谤狡、方法是沒有返回值的,是void的
* 3卧檐、方法名稱自定義
* 4墓懂、方法有參數(shù)是Exception
*/
/**
* @AfterThrowing:是異常通知’
* 屬性:value:切入點(diǎn)表達(dá)式
* throwing:自定義變量,表示目標(biāo)方法拋出的異常霉囚,變量名必須和通知方法的形參名一樣
* 位置:在方法的上面
*
* 特點(diǎn):
* 1捕仔、在目標(biāo)方法拋出異常后執(zhí)行的,沒有異常不執(zhí)行
* 2盈罐、能夠獲取目標(biāo)方法的異常信息
* 3榜跌、不是異常處理程序,可以得到發(fā)生異常的通知盅粪,可以發(fā)送郵件钓葫、短信等通知開發(fā)人員
* 可以看做是目標(biāo)方法的監(jiān)控程序
* 異常通知的執(zhí)行
* try{
* SomeServiceImpl.doSecond(..)
* }catch(Exception e){
* myAfterThrowing(e)
* }
*/
@AfterThrowing(value = "execution(* *..SomeServiceImpl.doSecond(..))",throwing="ex")
public void myAfterThrowing(Exception ex){
System.out.println("異常通知,在目標(biāo)方法拋出異常時(shí)候執(zhí)行的票顾,異常原因?yàn)椋?+ex.getMessage());
/**
* 異常發(fā)生時(shí):
* 1础浮、記錄異常發(fā)生的時(shí)間、位置等信息
* 2奠骄、發(fā)送郵件豆同、短信等通知開發(fā)人員
*/
}
}
3.3.7@After 最終通知
語法:@After(value="切入點(diǎn)表達(dá)式")
package com.wh.ch11after.handle;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import java.util.Date;
/**
* @Author :wanghui
* @Date 2022/1/2 - 9:35
*/
@Aspect
public class MyAspect{
/**
* 最終通知方法的定義:
* 1、方法是public
* 2含鳞、方法是沒有返回值的影锈,是void的
* 3、方法名稱自定義
* 4蝉绷、方法有參數(shù)是Exception
*/
/**
* @After:最終通知
* 屬性:value:切入點(diǎn)表達(dá)式
* 位置:在方法的上邊
*
* 特點(diǎn):
* 1鸭廷、在目標(biāo)方法之后執(zhí)行的
* 2、總是會(huì)被執(zhí)行
* 3潜必、可用用來做程序最后的收尾工作靴姿,例如:清楚臨時(shí)數(shù)據(jù)、變量磁滚、清理內(nèi)存
*
* 最終通知
* try{
* 業(yè)務(wù)方法
* }finally{
* myAfter
* }
*/
@After(value = "execution(* *..SomeServiceImpl.doThird(..))")
public void myAfter(){
System.out.println("最終通知佛吓,總是會(huì)被執(zhí)行");
}
}
3.3.8 @Pointcut
定義和管理切入點(diǎn)的注解
@PointCcut(value="切入點(diǎn)表達(dá)式")
package com.wh.handle;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import java.util.Date;
/**
* @Author :wanghui
* @Date 2022/1/2 - 9:35
*/
@Aspect
public class MyAspect{
@Before(value = "mypt()")
public void myBefore(){
System.out.println("前置方法=========");
}
@After(value = "mypt()")
public void myAfter(){
System.out.println("最終通知宵晚,總是會(huì)被執(zhí)行");
}
/**
* @pointcut:管理和定義切入點(diǎn)的,不是通知注解
* 屬性:value,切入點(diǎn)表達(dá)式
* 位置维雇,在一個(gè)自定義方法的上面淤刃,這個(gè)方法看做是切入點(diǎn)表達(dá)式的別名
* 其他的通知注解中,可以使用方法別名吱型,就表示使用這個(gè)切入點(diǎn)表達(dá)式了
*/
@Pointcut("execution(* *..SomeServiceImpl.doThird(..))")
public void mypt(){
//無需代碼
}
}
3.4 AOP 總結(jié)
AOP是一種動(dòng)態(tài)的思想逸贾,目的是實(shí)現(xiàn)業(yè)務(wù)功能和非業(yè)務(wù)功能的解耦合。業(yè)務(wù)功能是獨(dú)立的模塊津滞,其他功能也是獨(dú)立的模塊铝侵。例如事務(wù)功能,日志功能等等触徐。讓這些事務(wù)咪鲜、日志功能是可以被復(fù)用的。
當(dāng)目標(biāo)方法需要一些功能時(shí)撞鹉,可以在不修改疟丙、不能修改源代碼的情況下,使用AOP技術(shù)鸟雏,在程序執(zhí)行期間享郊,生成代理對(duì)象,通過代理執(zhí)行我們的業(yè)務(wù)方法孝鹊,同時(shí)增加功能炊琉。