天天看點

SpringAOP-什麼是面向切面程式設計?

一.什麼是面向切面程式設計

  • AOP簡介

AOP為Aspect Oriented Programming的縮寫,意為:

面向切面程式設計

,通過

預編譯

方式和運作期間動态代理實作程式功能的統一維護的一種技術。

  • 為什麼使用AOP程式設計範式?

分離功能性需求和非功能性需求

集中處理某一關注點

侵入性少,增強代碼可讀性及可維護性

  • AOP應用場景

權限控制、緩存控制、事務控制、分布式追蹤、異常處理等

  • 舉個栗子

如果你要在Service層的某些特定方法需加上權限驗證,使用OOP思想的話隻能在方法内部添加驗證身份的代碼,例如

public void insert() {
  checkUserAdmin.check();    //加入權限驗證方法
  repository.insert();        //調用dao層插入資料庫一條記錄
}           

這樣看起來功能是實作了,但如果service層有很多insert和delete方法呢?這樣插入代碼的方式不易于我們去統一管理,且修改了原代碼,具有侵入性。

那麼使用了AOP之後呢?你可以建一個切面類,對要進行權限驗證的方法進行切入。

即在程式運作時,動态地将代碼切入到類的指定方法或位置上的思想,就是面向切面程式設計。

二.AOP常用術語

  • 要想使用面向對象程式設計的思想,首先要了解幾個專有名詞

• Target:目标類,即需要被代理的類。例如:UserService

• Joinpoint(連接配接點):所謂連接配接點是指那些可能被攔截到的方法。例如:所有的方法

• PointCut 切入點:已經被增強的連接配接點。例如:addUser()

• Advice 通知/增強,增強代碼。例如:after、before

• Weaving(織入):是指把增強advice應用到目标對象target來建立新的代理對象proxy的過程.

• Proxy 代理類

• Aspect(切面): 是切入點pointcut和通知advice的結合

三.Advice-五種增強方式

  • 例如在執行某個特定方法的時候,我們可以選擇不同的增強方式(如前置通知/增強,在方法運作前執行),達到我們織入後的不同效果。

前置通知:在我們執行目标方法之前運作(@Before)

@Pointcut("within(com.example.demo.Service.*)")
    public void matchType(){}

    @Before("matchType()")    //可在此加入JoinPoint列印切點資訊
    public void before(JoinPoint joinPoint){
        System.out.println("------【前置通知】------" + joinPoint);
    }           

後置通知:在我們目标方法運作結束之後 ,不管有沒有異常(@After)

@After(value="execution(* com.example.aspectJ.demo1.ProductDao.findAll(..))")
    public void after(){
        System.out.println("最終通知==================");           

傳回通知:在我們的目标方法正常傳回值後運作(@AfterReturning)

@AfterReturning(value="execution(* com.example.aspectJ.demo1.ProductDao.update(..))" ,returning = "result")
    public void afterReturning(Object result){    //通過returning屬性,定義方法傳回值作為參數
        System.out.println("後置通知========="+result);
    }           

異常通知:在我們的目标方法出現異常後運作(@AfterThrowing)

//通過設定throwing屬性,可以設定發生異常對象參數
@AfterThrowing(value = "execution(* com.example.aspectJ.demo1.ProductDao.findOne(..))",throwing = "e")
    public void afterThrowing(Throwable e){
        System.out.println("抛出異常通知"+e.getMessage());
    }           

環繞通知:動态代理, 需要手動執行joinPoint.procced()(其實就是執行我們的目标方法執行之前相當于前置通知, 執行之後就相當于我們後置通知(@Around)

@Around(value = "execution(* com.example.aspectJ.demo1.ProductDao.delete(..))")
  public Object around(ProceedingJoinPoint joinPoint) throws Throwable {

        System.out.println("環繞前通知");
        Object obj = joinPoint.proceed(); //執行目标方法
        System.out.println("環繞後通知");
        return obj;
  }           

四.SpringAOP使用詳解

切面表達式

  • 下面是一張思維導圖總結
SpringAOP-什麼是面向切面程式設計?

excution表達式

  • execution(
    • 修飾符pattern
    • 傳回值pattern
    • 描述包名
    • 方法名(參數)
    • 方法抛出異常pattern

)

  • 代碼示例
    @Pointcut("execution(public * com.example.controller.*Controller.*(..))")
    public void match(){}
    
    @Before("match()")
    public void before(){
      //前置通知...
    }           

within表達式

  • //比對StudentService類裡所有方法
    @Pointcut("within(com.example.service.StudentService)")
    public void matchType(){}
    
    //比對com.example包及子包下所有類方法
    @Pointcut("within(com.example..*)")
    public void matchPackage(){}           

對象比對

  • /*public class serviceImpl implements service*/
    
    //比對AOP對象的目标對象為指定類型方法,即serviceImpl的aop代理對象方法
    @Pointcut("this(com.example.serviceImpl)")
    public void thisDemo(){}
    
    //比對實作service接口的目标對象(非aop代理後的對象)方法,這裡指的就是serviceImpl的方法
    @Pointcut("target(com.example.service)")
    public void targetDemo(){}
    
    //比對所有以Service結尾的bean中方法
    @Pointcut("bean(*Service)")
    public void beanDemo(){}
               

參數比對

  • //比對任何以find開頭且隻有一個Long參數的方法
    @Pointcut("execution(* *..find*(Long))")
    public void argsDemo1(){}
    
    //比對任何隻有一個Long參數的方法
    @Pointcut("args(Long)")
    public void argsDemo2(){}
    
    //比對任何以find開頭且第一個參數為Long的方法
    @Pointcut("execution(* *..find*(Long,..))")
    public void argsDemo3(){}
    
    //比對第一個參數為Long的方法
    @Pointcut("args(Long,..)")
    public void argsDemo4(){}
               

注解比對

  • //比對方法注解有@AdminOnly的方法
    @Pointcut("@annotation(com.example.security.AdminOnly)")
    public void annoDemo(){}
    
    //比對注解有@Test1的類下所有方法,要求注解的RetentionPolicy級别為CLASS
    @Pointcut("@within(com.example.annotation.Test1)")
    public void annoWithinDemo(){}
    
    //比對注解有@Test2類下所有方法,要求注解的RetentionPolicy級别為RUNTIME
    @Pointcut("@target(com.example.repository.Test2)")
    public void annoTargetDemo(){}
    
    //比對傳入參數類具有@Test3的注解的方法(例如student實體類有注解@Test3,隻要方法傳入student類就會被攔截)
    @Pointcut("@args(org.example.repository.Test3)")
    public void annoArgsDemo(){}
               

​ 至于AOP的實作原理,這裡暫時不講,有興趣的可以去了解下jdk的動态代理,AOP就是基于此實作的。

更多内容,歡迎通路我的:

515code部落格 bilibili視訊 Github