2016年1月17日 星期日

AOP1-before after (Spring3.x 十八)

AOP簡單來說就是假如有一行程式的前面或後面,想增加固定的程式,但又不想讓這支程式知道,所以通過設定的方式來實現

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步寫的aop

BeforeAfter.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方法了


沒有留言:

張貼留言