Aspect Oriented Programing 面向切面編程,相比較 oop 面向?qū)ο缶幊虂碚f,Aop 關(guān)注的不再是程序代碼中某個類,某些方法,而 aop 考慮的更多的是一種面到面的切入,即層與層之間的一種切入,所以稱之為切面。聯(lián)想大家吃的漢堡(中間夾肉)。那么 aop 是怎么做到攔截整個面的功能呢?考慮學(xué)到的 servlet urlpattern /* 的配置,實際上也是 aop 的實現(xiàn) 。
Spring Aop 實現(xiàn)的方式
- 注解 方式
- XML 方式
案例實操
注解方式
jar 包坐標(biāo)引入
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
beans.xml 配置
添加命名空間
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
配置 Aop 代理
<aop:aspectj-autoproxy/>
編寫 aop 實現(xiàn)類
/**
* 聲明切面組件
*/
@Component
@Aspect
public class LogCut {
/**
* 定義切入點 匹配方法規(guī)則定義
* 匹配規(guī)則表達(dá)式含義 攔截 com.xxx.service 包下 以及子包下 所有類的所有方法
*/
@Pointcut("execution (* com.xxx.service..*.*(..))")
public void cut(){}
/**
* 聲明前置通知 并將通知應(yīng)用到定義的切入點上
* 目標(biāo)類方法執(zhí)行前 執(zhí)行該通知
*/
@Before(value="cut()")
public void before(){
System.out.println("前置通知.....");
}
/**
* 聲明返回通知 并將通知應(yīng)用到切入點上
* 目標(biāo)類方法執(zhí)行完畢執(zhí)行該通知
*/
@AfterReturning(value="cut()")
public void afterReturning(){
System.out.println("返回通知....");
}
/**
* 聲明最終通知 并將通知應(yīng)用到切入點上
* 目標(biāo)類方法執(zhí)行過程中是否發(fā)生異常 均會執(zhí)行該通知 相當(dāng)于異常中的 finally
*/
@After(value="cut()")
public void after(){
System.out.println("最終通知....");
}
/**
* 聲明異常通知 并將通知應(yīng)用到切入點上
* 目標(biāo)類方法執(zhí)行時發(fā)生異常 執(zhí)行該通知
*/
@AfterThrowing(value="cut()",throwing="e")
public void afterThrowing(Exception e){
System.out.println("異常通知....方法執(zhí)行異常時執(zhí)行:"+e);
}
/**
* 聲明環(huán)繞通知 并將通知應(yīng)用到切入點上
* 方法執(zhí)行前后 通過環(huán)繞通知定義相應(yīng)處理
*/
@Around(value="cut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("環(huán)繞前置...");
System.out.println("環(huán)繞通知");
System.out.println(pjp.getTarget()+"--"+pjp.getSignature());
Object result=pjp.proceed();//執(zhí)行目標(biāo)對象方法
System.out.println("環(huán)繞后置...");
return result;
}
}
Aop 匹配方法規(guī)則表達(dá)式語言(簡要了解)
Aop 切入點表達(dá)式簡介
執(zhí)行任意公共方法:
execution(public *(..))
執(zhí)行任意的 set 方法
execution(* set*(..))
執(zhí)行 com.xxx.service 包下任意類的任意方法
execution(* com.xxx.service.*.*(..))
執(zhí)行 com.xxx.service 包 以及子包下任意類的任意方法
execution(* com.xxx.service..*.*(..))
xml 方式
配置切面、切入點、通知
<!-- aop 相關(guān)配置 -->
<aop:config>
<!-- aop 切面配置 -->
<aop:aspect ref="logCut">
<!-- 定義 aop 切入點 -->
<aop:pointcut expression="execution (* com.xxx.service..*.*(..))"
id="cut"/>
<!-- 配置前置通知 指定前置通知方法名 并引用切入點定義 -->
<aop:before method="before" pointcut-ref="cut"/>
<!-- 配置返回通知 指定返回通知方法名 并引用切入點定義 -->
<aop:after-returning method="afterReturning" pointcut-ref="cut"/>
<!-- 配置異常通知 指定異常通知方法名 并引用切入點定義 -->
<aop:after-throwing method="afterThrowing" throwing="e" pointcut-ref="cut"/>
<!-- 配置最終通知 指定最終通知方法名 并引用切入點定義 -->
<aop:after method="after" pointcut-ref="cut"/>
<!-- 配置環(huán)繞通知 指定環(huán)繞通知方法名 并引用切入點定義 -->
<aop:around method="around" pointcut-ref="cut"/>
</aop:aspect>
</aop:config>
定義 bean
/**
* 聲明切面組件
*/
@Component
public class LogCut {
public void before(){
System.out.println("前置通知.....");
}
public void afterReturning(){
System.out.println("返回通知....");
}
public void after(){
System.out.println("最終通知....");
}
public void afterThrowing(Exception e){
System.out.println("異常通知....方法執(zhí)行異常時執(zhí)行:" + e);
}
public Object around(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("環(huán)繞前置...");
System.out.println("環(huán)繞通知");
System.out.println(pjp.getTarget()+"--"+pjp.getSignature());
Object result=pjp.proceed();
System.out.println("環(huán)繞后置...");
return result;
}
}
擴展
AOP 的基本概念
JoinPoint(連接點)【動態(tài)】
被攔截到的每個點,spring 中指被攔截到的每一個方法,spring aop 一個連接點即代表一個方法的執(zhí)行。
Pointcut(切入點)【靜態(tài)】
對連接點進(jìn)行攔截的定義(匹配規(guī)則定義 規(guī)定攔截哪些方法,對哪些方法進(jìn)行處理),spring 這塊有專門的表達(dá)式語言定義。
Advice(通知){重點}
攔截到每一個連接點即(每一個方法)前后所要做的操作
- 前置通知(前置增強)--before() 執(zhí)行方法前通知
- 返回通知(返回增強)--afterReturning 方法正常結(jié)束返回后的通知
- 異常拋出通知(異常拋出增強)--afetrThrow()
- 最終通知 --after 無論方法是否發(fā)生異常,均會執(zhí)行該通知
- 環(huán)繞通知 --around 包圍一個連接點(join point)的通知,如方法調(diào)用。這是最強大的一種通知類型。 環(huán)繞通知可以在方法調(diào)用前后完成自定義的行為。它也會選擇是否繼續(xù)執(zhí)行連接點或直接返回它們自己的返回值或拋出異常來結(jié)束執(zhí)行
Aspect(切面)
切入點與通知的結(jié)合,決定了切面的定義,切入點定義了要攔截哪些類的 哪些方法,通知則定義了攔截方法后要做什么,切面則是橫切關(guān)注點的抽象,與類相似,類是對物體特征的抽象,切面則是橫切關(guān)注點抽象。
Target(目標(biāo)對象)
被代理的目標(biāo)對象
Weave(織入)
將切面應(yīng)用到目標(biāo)對象并生成代理對象的這個過程即為織入(過程)。
Introduction(引入)
在不修改原有應(yīng)用程序代碼的情況下,在程序運行期為類動態(tài)添加方法或者字段的過程稱為引入。