aspectj入門

概述在學習spring aop過程當中,發現有個怎麼都繞不過去的坎,就是AspectJ的使用。少了這一部分,一些spring aop的源碼總以爲少了點什麼,看不大懂。因此接下來會寫幾篇關於AspectJ的入門使用。
安裝示例代碼一、首先,下載最新的穩定版本(我本身下的是AspectJ 1.9.2, Released 24 Oct 2018 版本)aspectj-1.9.2.jar
http://www.eclipse.org/aspect...
二、安裝
aspectJ的安裝很簡單,運行 java -jar aspectj-1.9.2.jar 而後選下jdk路徑及最終aspectj要安裝(AspecJ安裝路徑)到哪裏就好
三、安裝最後,會提示你要配置環境變量了php

按照環境配下PATH變量 和 CLASSPATH變量就好
export PATH=${JAVA_HOME}/bin:$PATH:/Users/hdj/software/aspectj/binexport CLASSPATH=${CLASSPATH}:/Users/hdj/software/aspectj/lib/aspectjrt.jar四、編譯代碼示例
進入 ${aspectJ 安裝路徑}/doc/examples
執行   ajc -argfile telecom/billing.lst
五、運行示例
java telecom.BillingSimulation六、運行結果
[Items [id=1, name=臺式機, price=3000.0, pic=null, createtime=Tue Oct 03 23:22:36 CST 2017, detail=該電腦質量很是好!!!!111], Items [id=2, name=筆記本, price=7000.0, pic=null, createtime=Tue Oct 03 23:23:06 CST 2017, detail=筆記本性能好,質量好!!!!!], Items [id=3, name=揹包, price=1200.0, pic=null, createtime=Tue Oct 03 23:23:21 CST 2017, detail=名牌揹包,容量大質量好!!!!]]java

示例總結簡單總結下:
1)安裝配置aspectJ環境
2)使用 ajc -argfile  編譯類
3)運行編譯後的字節碼
上述的例子是aspectJ 官方的例子 ,爲了說明如何使用aspectJ的使用,咱們看另外一個例子
另外一個例子知道如何運行aspectJ給的官方示例後,咱們繼續學習官網給的另外一個例子,例子路徑
${aspectJ 路徑}/doc/examples/tjp/Demo.java
咱們先來看看原始類
public class Demo {    static Demo d;    public static void main(String[] args){        new Demo().go();    }    void go(){        d = new Demo();        //調用foo方法        d.foo(1,d);        //調用bar方法        System.out.println(d.bar(new Integer(3)));    }    void foo(int i, Object o){        //打印foo        System.out.println("Demo.foo(" + i + ", " + o + ")n");    }    String bar (Integer j){        System.out.println("Demo.bar(" + j + ")n");        return "Demo.bar(" + j  + ")";    }}再來看看一個隨着Demo.java一塊兒被 ajc編譯的特殊的類GetInfo.java:
package tjp;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.reflect.CodeSignature;aspect GetInfo {  static final void println(String s){ System.out.println(s); }    pointcut goCut(): cflow(this(Demo) && execution(void go()));  pointcut demoExecs(): within(Demo) && execution( (..));  Object around(): demoExecs() && !execution(* go()) && goCut() {     println("Intercepted message: " +         thisJoinPointStaticPart.getSignature().getName());     println("in class: " +         thisJoinPointStaticPart.getSignature().getDeclaringType().getName());     printParameters(thisJoinPoint);     println("Running original method: n" );     Object result = proceed();     println("  result: " + result );     return result;  }  // 打印參數  static private void printParameters(JoinPoint jp) {     println("Arguments: " );     Object[] args = jp.getArgs();     String[] names = ((CodeSignature)jp.getSignature()).getParameterNames();     Class[] types = ((CodeSignature)jp.getSignature()).getParameterTypes();     for (int i = 0; i < args.length; i++) {        println("  "  + i + ". " + names +            " : " +            types.getName() +            " = " +            args);     }  }}這裏和spring aop的使用不少方面很是相似,最後執行的結果以下:spring

這裏說明一下幾點:
GetInfo.java 含有的特殊關鍵詞GetInfo.java含有不少特殊的關鍵詞,這些關鍵詞java是沒法識別的,須要經過ajc編譯才能織入到字節碼中
切入點表達式spring的切入點表達式咱們很是熟悉,使用aspectJ時,也須要配置切入點,須要配置有哪些方法須要被切入
//表示Demo類的go方法調用過程當中的方法須要被加強pointcut goCut(): cflow(this(Demo) && execution(void go()));//每個Demo類定義的方法pointcut demoExecs(): within(Demo) && execution( (..));// 因爲 goCut() 這個切入點還包含go()方法本身的調用,所以須要把本身給排掉Object around(): demoExecs() && !execution(* go()) && goCut();可能會問,既然爲啥要同時配置
demoExecs() 以及 !execution(* go()) && goCut() 單獨有一個不夠麼?
1)demoExecs()表示攔截Demo類的全部方法,若是不加後面的限制,會同時攔截main方法,以及go()方法
2)若是隻有!execution(* go()) && goCut(),直接編譯時候會報錯(warning)執行後直接會報java.lang.StackOverflowError
3)若是隻有!execution(* go()) && goCut(),能夠執行,可是隻會攔截go方法
thisJoinPointStaticPart 和 thisJoinPointthisJoinPoint 包含了關於當前切入點的一些信息(經過反射獲取的)
thisJoinPointStaticPart 包含一些靜態信息,參考官方文檔
經過配置切入點,咱們實現了不改變Demo.java源碼的前提下,往Demo.java方法的先後插入了一段代碼。
與Spring 切面寫法的對比對比下以前在學習Spring時候,配置的切面
//聲明這是一個組件@Component//聲明這是一個切面Bean@Aspect@Slf4jpublic class ServiceAspect {    //配置切入點,該方法無方法體,主要爲方便同類中其餘方法使用此處配置的切入點    @Pointcut("execution( com.hdj.learn.spring.aop.service..(..))")    public void aspect() {    }    /      配置前置通知,使用在方法aspect()上註冊的切入點      同時接受JoinPoint切入點對象,能夠沒有該參數     /    @Before("aspect()")    public void before(JoinPoint joinPoint) {        log.info("before " + joinPoint);    }    //配置後置通知,使用在方法aspect()上註冊的切入點    @After("aspect()")    public void after(JoinPoint joinPoint) {        log.info("after " + joinPoint);    }    //配置環繞通知,使用在方法aspect()上註冊的切入點    @Around("aspect()")    public void around(JoinPoint joinPoint) {        long start = System.currentTimeMillis();        try {            ((ProceedingJoinPoint) joinPoint).proceed();            long end = System.currentTimeMillis();            log.info("around " + joinPoint + "tUse time : " + (end - start) + " ms!");        } catch (Throwable e) {            long end = System.currentTimeMillis();            log.info("around " + joinPoint + "tUse time : " + (end - start) + " ms with exception : " + e.getMessage());        }    }    //配置後置返回通知,使用在方法aspect()上註冊的切入點    @AfterReturning("aspect()")    public void afterReturn(JoinPoint joinPoint) {        log.info("afterReturn " + joinPoint);    }    //配置拋出異常後通知,使用在方法aspect()上註冊的切入點    @AfterThrowing(pointcut = "aspect()", throwing = "ex")    public void afterThrow(JoinPoint joinPoint, Exception ex) {        log.info("afterThrow " + joinPoint + "t" + ex.getMessage());    }}其實很是相似,有切面、有通知、有目標類等等,切入點的表達式也很是相似。
總結下初步瞭解了aspectJ的使用,咱們能夠了解如下幾點:
1)aspectJ的使用是在編譯期,經過特殊的編譯器能夠在不改變代碼的前提下織入代碼(固然能不能在運行期,我尚未確認)
2)aspectJ的使用,也是配置切入點、通知eclipse

相關文章
相關標籤/搜索