Spring 的 AOP 是 runtime 等級的,有些是 compile 等級的
runtime 是以方法為基底,如在方法前後加程式碼;而 compile 可以更細,如在 某個方法裡的if 之前/後、for 迴圈之前/後
※1.先寫一支IOC,而且確定能run
IAOP.java和AOP.java
public interface IAOP { public void A(); public void B(String b); public String C(); public String D(String d); } @Named public class AOP implements IAOP { @Override public void A() { System.out.println("無參數A"); } @Override public void B(String b) { System.out.println("有參數B"); } @Override public String C() { System.out.println("無參數C"); return "c"; } @Override public String D(String d) { System.out.println("有參數D"); return d; } }
測試類
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); for (String s : ctx.getBeanDefinitionNames()) { System.out.println(s); } IAOP aop = ctx.getBean("AOP", IAOP.class); aop.A(); aop.B("bbb"); aop.C(); aop.D("ddd"); ((ClassPathXmlApplicationContext) ctx).close();
※在applicationContext.xml加<context:component-scan base-package="\" />,然後執行,沒有錯再做第2步
※2.加入AOP
寫一支前後包住第1步寫的aopBeforeAfter.java
@Named public class BeforeAfter { public void before() { System.out.println("之前執行!"); } public void after() { System.out.println("之後執行!"); } }
applicationContext.xml
<aop:config> <aop:aspect ref="beforeAfter"> <aop:before method="before" pointcut="execution(public void aop.AOP.A())" /> <aop:after method="after" pointcut="execution(public void aop.AOP.A())" /> <aop:before method="before" pointcut="execution(public void aop.AOP.B(String))" /> <aop:after method="after" pointcut="execution(public void aop.AOP.B(String))" /> <aop:before method="before" pointcut="execution(public String aop.AOP.C())" /> <aop:after method="after" pointcut="execution(public String aop.AOP.C())" /> <aop:before method="before" pointcut="execution(public String aop.AOP.D(String))" /> <aop:after method="after" pointcut="execution(public String aop.AOP.D(String))" /> </aop:aspect> </aop:config>
※結果:
之前執行!
無參數A
之後執行!
之前執行!
有參數B
之後執行!
之前執行!
無參數C
之後執行!
之前執行!
有參數D
之後執行!
※execution的語法和一般寫java的方法很像,可以參考官方完整的語法,但實在是太多了,已經有很厲害的人整理出來了,可參考這裡,我只大概了解一些,因為我也不太懂,有可能講錯,只是例子是真的有run過就是了
.格式為:<修飾子> 回傳值 包.類.方法(參數) <異常>
.一定要用execution()包起來
.修飾子和異常可以不打,「*」代表全部,修飾子不打就是*,異常我不會用
.修飾子就是public、protected、private,預設我不清楚
.回傳值打「*」,代表任何回傳值
.方法打「*」,代表任何方法
.包.類打「*」,代表任何包.類
.包.類有個特殊的「..*」,譬如aop.*,表示aop下一層的任何類,但有可能有很多層,所以如果下aop..*就表示aop以下的任何類
.參數如果打「*」或「..」,表示任何參數,我看大部分都打..,很少人打*,但我試過是ok的
.execution(* aop..*.*(..)),這表示在aop以下的任何方法的任何參數,回傳值也是任何的就執行aop
上面的範例,方法ABCD的execution都用到兩次,很浪費,所以還可以把它提出來,變成以下的樣子
<aop:config> <aop:pointcut expression="execution(public void aop.AOP.A())" id="aaa" /> <aop:pointcut expression="execution(public void aop.AOP.B(String))" id="bbb" /> <aop:pointcut expression="execution(public String aop.AOP.C())" id="ccc" /> <aop:pointcut expression="execution(public String aop.AOP.D(String))" id="ddd" /> <aop:aspect ref="beforeAfter"> <aop:before method="before" pointcut-ref="aaa" /> <aop:after method="after" pointcut-ref="aaa" /> <aop:before method="before" pointcut-ref="bbb" /> <aop:after method="after" pointcut-ref="bbb" /> <aop:before method="before" pointcut-ref="ccc" /> <aop:after method="after" pointcut-ref="ccc" /> <aop:before method="before" pointcut-ref="ddd" /> <aop:after method="after" pointcut-ref="ddd" /> </aop:aspect> </aop:config>
※結果當然還是一樣
※也可以用剛剛講的AspectJ語法,一次使用
<aop:config> <aop:pointcut expression="execution(* aop.AOP.*(..))" id="abcd" /> <aop:aspect ref="beforeAfter"> <aop:before method="before" pointcut-ref="abcd" /> <aop:after method="after" pointcut-ref="abcd" /> </aop:aspect> </aop:config>
※結果也是一樣
※這時候來了解一下名詞就會比較容易了解
.Join Point:BeforeAfter.java裡面的方法都是,可以想像成要連到主程式的連接點
.Point Cut:就是xml裡的<aop:pointcut/>,而裡面的屬性expression叫Point Cut表達式
.Advice:就是xml裡的<aop:before>等…,通知Point Cut匹配的方法之前(或之後…等)要執行Join Point裡的方法
※例外
如果程序有例外的情形又是怎麼樣呢?將程式碼改成以下的樣子IAOP.java和AOP.java
public interface IAOP { public void A() throws Exception; public void B(String b); public String C(); public String D(String d); } @Named public class AOP implements IAOP { @Override public void A() throws Exception { System.out.println("無參數A"); int a = 1 / 0; } @Override public void B(String b) { System.out.println("有參數B"); try { int a = 1 / 0; } catch (Exception e) { System.out.println("我錯了B"); } } @Override public String C() { System.out.println("無參數C"); int a = 1 / 0; return "c"; } @Override public String D(String d) { System.out.println("有參數D"); int a = 1 / 0; return d; } }
測試類
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); IAOP aop = ctx.getBean("AOP", IAOP.class); try { aop.A(); } catch (Exception e) { System.out.println("例外出現"); } aop.B("bbb"); aop.C(); aop.D("ddd"); ((ClassPathXmlApplicationContext) ctx).close();
※1/0是RuntimeException,可以不用try/catch,A和B方法用不同的方法包起來,C和D方法都不包,結果如下:
之前執行!
無參數A
之後執行!
例外出現
之前執行!
有參數B
我錯了B
之後執行!
之前執行!
無參數C
之後執行!
Exception in thread "main" java.lang.ArithmeticException: / by zero
//...
可以看出ABC可以正常出現之前之後,但因為C方法不包try/catch,所以不會繼續往下執行D方法了
沒有留言:
張貼留言