Aop
- AOP(Aspect-Oriented Programming,面向切面編程):是一種新的方法論,是對傳統OOP(Object-Oriented Programming,面向對象編程)的補充。
- AOP編程操作的主要對象是切面(aspect),而切面模塊化橫切關注點(可以把切面當做一個有特殊功能的類)
- 在應用AOP編程時,仍然需要定義公共功能,但可以明確的定義這個功能應用在哪裏,以什麼方式應用,並且不必修改受影響的類。這樣一來橫切關注點就被模塊化到特殊的類裏——這樣的類我們通常稱之爲“切面”
專業名詞
- 橫切關注點
從每個方法中抽取出來的同一類非核心業務。(例如上圖的日誌) - 切面(Aspect)
封裝橫切關注點信息的類,每個關注點體現爲一個通知方法。 - 通知(Advice)
切面必須要完成的各個具體工作 - 目標(Target)
被通知的對象 - 代理(Proxy)
向目標對象應用通知之後創建的代理對象 - 連接點(Joinpoint)
橫切關注點在程序代碼中的具體體現,對應程序執行的某個特定位置。例如:類某個方法調用前、調用後、方法捕獲到異常後等。
在應用程序中可以使用橫縱兩個座標來定位一個具體的連接點:
7. 切入點(pointcut):
定位連接點的方式。每個類的方法中都包含多個連接點,所以連接點是類中客觀存在的事物。如果把連接點看作數據庫中的記錄,那麼切入點就是查詢條件——AOP可以通過切入點定位到特定的連接點。切點通過org.springframework.aop.Pointcut 接口進行描述,它使用類和方法作爲連接點的查詢條件。
細節
切入點表達式:通過表達式去定位一個或多個具體的連接
語法格式:execution([權限修飾符] [返回值類型] [簡單類名/全類名] [方法名]([參數列表]))
當前連接點所在方法的方法名、當前傳入的參數值等等。這些信息都封裝在JoinPoint接口的實例對象中
@Component //先標示爲組件,交給IOC容器管理
@Aspect //標識爲切面
@Order(2)
public class LoggingAspect {
/**
* 前置通知:在目標方法執行之前執行的方法
* 注意導包的時候不要導錯了
* 切入點表達式
* execution(public int Aspectj.Before.ArithmeticCalculatorimpl.add(int,int))
*聲明切入點表達式
*/
@Pointcut("execution(* Aspectj.Before.*.*(..))")
public void declarePointCut(){
}
// @Before("execution(public int Aspectj.Before.ArithmeticCalculatorimpl.add(int,int))")
@Before("declarePointCut()")
public void beforeMethod(JoinPoint joinPoint){
Object args[]=joinPoint.getArgs();
System.out.println("前置通知:"+ Arrays.asList(args));
}
/**
* 後置通知:在目標方法之後執行,不管目標方法有沒有拋出異常
* * * com.atguigu.spring.aspectJ.annotation.*.*(..)
* * : 任意修飾符 任意返回值
* * : 任意類
* * : 任意方法
* ..: 任意參數列表
*
*/
@After("execution(* Aspectj.Before.*.*(..))")
public void AfterMethod(JoinPoint joinPoint){
//方法的名字
String methodName=joinPoint.getSignature().getName();
System.out.println("後置通知:"+methodName);
}
/**
* 返回通知:在目標方法正常執行結束之後執行,可以獲取方法的返回值
* 通過returning來制定一個名字,必須與方法裏的形參一樣
*/
@AfterReturning(value="execution(* Aspectj.Before.*.*(..))",returning = "result")
public void AfterReturnMethod(JoinPoint joinPoint,Object result){
//方法的名字
String methodName=joinPoint.getSignature().getName();
System.out.println("返回通知:"+methodName+"返回結果"+result);
}
@AfterThrowing(value = "execution(* Aspectj.Before.*.*(..))",throwing = "ex")
public void AfterThrowMethod(JoinPoint joinPoint,Exception ex){
//方法的名字
String methodName=joinPoint.getSignature().getName();
System.out.println("異常通知:"+ex);
}
/**
* 環繞通知:環繞着目標方法執行,可以理解爲前置,後置,返回,異常的結合體更像是動態代理的過程
*/
@Around(value = "execution(* Aspectj.Before.*.*(..))")
public Object AroundMethod(ProceedingJoinPoint joinPoint){
//執行目標放阿飛
try {
//前置
Object obj=joinPoint.proceed();
return obj;
//返回
}catch (Throwable e){
e.printStackTrace();
}finally {
//後置
}
return null;
}
}
Aspectj(Java社區裏最完整的AOP框架)
所需jar包
com.springsource.net.sf.cglib-2.2.0.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
spring-aop-5.2.0.RELEASE.jar
spring-aspects-5.2.0.RELEASE.jar
命名空間
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
重用切入點定義
1)在編寫AspectJ切面時,可以直接在通知註解中書寫切入點表達式。但同一個切點表達式可能會在多個通知中重複出現。
2)在AspectJ切面中,可以通過@Pointcut註解將一個切入點聲明成簡單的方法。切入點的方法體通常是空的,因爲將切入點定義與應用程序邏輯混在一起是不合理的。
3)切入點方法的訪問控制符同時也控制着這個切入點的可見性。如果切入點要在多個切面中共用,最好將它們集中在一個公共的類中。在這種情況下,它們必須被聲明爲public。在引入這個切入點時,必須將類名也包括在內。如果類沒有與這個切面放在同一個包中,還必須包含包名。
4)其他通知可以通過方法名稱引入該切入點
代碼實現:
@Pointcut("execution(* Aspectj.Before.*.*(..))")
public void declarePointCut(){
}
// @Before("execution(public int Aspectj.Before.ArithmeticCalculatorimpl.add(int,int))")
@Before("declarePointCut()")
在不同的文件裏面調用
//@Before("execution(* Aspectj.Before.*.*(..))")
@Before("Aspectj.Before.LoggingAspect.declarePointCut()")
指定切面優先級
1)在同一個連接點上應用不止一個切面時,除非明確指定,否則它們的優先級是不確定的。
2)切面的優先級可以通過實現Ordered接口或利用@Order註解指定。
3)實現Ordered接口,getOrder()方法的返回值越小,優先級越高。
@Component
@Aspect
@Order(1) //設置切面優先級,int類型的值,2147483647