Java裏使用AspectJ實現AOP

 前言

已經正式從NET轉型JAVA。今後開始多寫一點JAVA相關的文章。

因爲已經正式轉Java了,所以,對於Java的一些判斷,應該就比以前更準確了。總得來說,java有好的東西,有不好的東西,就語言本身和java的常用組件來講,並不能判斷,java比其他語言高一個檔次,當然,也不會低一個檔次。應該跟其他語言是一個段位的。

但java的調試,確實是比較花費時間,他做不到編譯成功後,就能運行成功。這裏有註解的問題,有maven的問題,有組件版本的問題。總之,檢測的非常不好,非常浪費時間。

java的好處就是,團隊成員比較多,畢竟開發起來真的很廢人。但好處也在這裏,人多,代表着,1,大家的壓力都不大,人多壓力就會分散。2,功能和性能有時間做的更優秀,人多就是工時多。

而且Java工資確實相對比其他語言高。

總體來說,java是比較幸福的。

開始正文

Aspectj提供一種在字符串裏編程的模式,即在字符串裏寫函數,然後程序啓動的時候會動態的把字符串裏的函數給執行了。

例如:

"execution(* *(..))"

這裏的execution就是一個函數,我們調用它,然後傳遞的參數是【* *(..)】。

Aspectj 使用
 
使用前,我們先了解一下execution和它的參數的匹配規則:
execution: 用於匹配方法執行的連接點;
execution(public * *(..)) ==> 匹配所有目標類的public方法,第一個*代表返回類型,第二個*代表方法名,而..代表任意入參的方法。
execution(* com.oysept.springboot.controller..*.*(..)) ==> 該包及所有子包下任何類的任何方法。
execution(* com.oysept.springboot.controller.*(..)) ==> 該包下任何類的任何方法。
execution(* com.oysept.springboot.controller.AspectJController.*(..)) ==> 該包下AspectJController類的任何方法。
execution(* com..*.*Controller.method*(..)) ==> 匹配包名前綴爲com的任何包下類名後綴爲Controller的方法,方法名必須以method爲前綴。
execution(* *To(..)) ==> 匹配目標類所有以To爲後綴的方法。
注: 該方法只是爲了聲明一個公共的環繞通知,也可以直接在具體方法配置,如: @Around("execution(* com.oysept.springboot.controller..*.*(..))")

@Before和@AfterReturning

然後我們編寫一個aspect的基礎使用代碼,如下:
/**
 * @Before:定義了前置通知方法。打印出入參
 * @AfterReturning:定義了後置返回通知方法。打印出入參、返參
 */
@Slf4j
@Aspect
@Component
public class AopAspect_Basic {  
    @Before("execution(public * com.k.tender.controller.business.user.UserController.*(..))")
    public void doBefore(JoinPoint point){
        String methodName = point.getSignature().getName();
        List<Object> args = Arrays.asList(point.getArgs());
        log.info("調用前連接點方法爲:" + methodName + ",參數爲:" + args);
    }

    @AfterReturning(value = "execution(public * com.k.tender.controller.business.user.UserController.*(..))", returning = "returnValue")
    public void doAfterReturning(JoinPoint point, Object returnValue){
        String methodName = point.getSignature().getName();
        List<Object> args = Arrays.asList(point.getArgs());
        log.info("調用前連接點方法爲:" + methodName + ",參數爲:" + args + ",返回值爲:" + returnValue);
    }

}

 如上代碼,我們使用了@Before和@AfterReturning註解,在UserController調用前和後,分別埋了點,並輸出了函數的入參和出參。

@Pointcut

@Pointcut其實是一個提取execution函數的操作,就是指定一個埋點,然後使用了@Before和@AfterReturning註解時,就不用每次都寫那個execution函數了,這樣就不用擔心寫錯了。
代碼示例如下:
  @Pointcut("execution(public * com.k.tender.controller.business.tender.TenderController.*(..))")
    public void doPointCut() {

    }

    @Before("doPointCut()")
    public void doBefore(JoinPoint point){
        String methodName = point.getSignature().getName();
        List<Object> args = Arrays.asList(point.getArgs());
        log.info("調用前連接點方法爲:" + methodName + ",參數爲:" + args);
    }

    @AfterReturning(value = "doPointCut()", returning = "returnValue")
    public void doAfterReturning(JoinPoint point, Object returnValue){
        String methodName = point.getSignature().getName();
        List<Object> args = Arrays.asList(point.getArgs());
        log.info("調用前連接點方法爲:" + methodName + ",參數爲:" + args + ",返回值爲:" + returnValue);
    }

對註解埋點

有時候,我們希望編寫一個註解,然後讓有該註解的函數,都被攔截,那麼就可以使用Aspectj的註解埋點模式。

代碼如下: 

@Slf4j
@Aspect
@Component
public class AopAspect_Annotation {

    @Before("@annotation(com.k.tender.aop.MyAop)")
    public void doBefore(JoinPoint point){
        String methodName = point.getSignature().getName();
        List<Object> args = Arrays.asList(point.getArgs());
        log.info("調用前連接點方法爲:" + methodName + ",參數爲:" + args);
    }
    @AfterReturning(value ="@annotation(com.k.tender.aop.MyAop)", returning = "returnValue")
    public void doAfterReturning(JoinPoint point, Object returnValue){
        String methodName = point.getSignature().getName();
        List<Object> args = Arrays.asList(point.getArgs());
        log.info("調用前連接點方法爲:" + methodName + ",參數爲:" + args + ",返回值爲:" + returnValue);
    }
}

public @interface MyAop {
  String value() default "自定義註解攔截";
}

如果覺得寫註解的命名空間麻煩,也可以這樣寫:

@Before("@annotation(apiOperation)")
    public void doBefore(JoinPoint point, MyAopAsyncTask apiOperation) {
        String methodName = point.getSignature().getName();
        List<Object> args = Arrays.asList(point.getArgs());
        log.info("調用前連接點方法爲:" + methodName + ",參數爲:" + args);
    }

還可以將註解和前面的excution函數結合寫:

  @Before("execution(public * com.k..*.*(..)) && @annotation(apiOperation)") 
    public void doBefore(JoinPoint point, MyAopAsyncTask apiOperation) throws NoSuchMethodException {
        String methodName = point.getSignature().getName();
        List<Object> args = Arrays.asList(point.getArgs());
        log.info("調用前連接點方法爲:" + methodName + ",參數爲:" + args); 
    }

有時候我們的攔截會觸發多次,這個具體原因調查起來很麻煩,我們也可以這樣解決,代碼如下:

 private volatile long hashcode = 0;//應對重複觸發
    @Before("execution(public * com.k..*.*(..)) && @annotation(apiOperation)")

    public void doBefore(JoinPoint point, MyAopAsyncTask apiOperation) throws NoSuchMethodException {
        if (hashcode != point.getTarget().hashCode()) {
            log.info("========doBefore========");
            ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = requestAttributes.getRequest();
            String method = request.getMethod();
           
        }

    }

使用hashcode來過濾多次攔截。

 ----------------------------------------------------------------------------------------------------

 到此,Android裏使用AspectJ實現AOP就介紹完了。

----------------------------------------------------------------------------------------------------

注:此文章爲原創,任何形式的轉載都請聯繫作者獲得授權並註明出處!
若您覺得這篇文章還不錯,請點擊下方的推薦】,非常感謝!

https://www.cnblogs.com/kiba/p/18027435

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章