Spring Aop Step-By-Step 學(xué)習(xí)筆記(上)
最近由于工作需要,要求掌握關(guān)于 Spring 方面的東西。所以花了兩個(gè)星期的時(shí)間來(lái)學(xué)習(xí) Spring 的基本知識(shí),主要包括 Ioc 和 Aop 兩方面。
本文為筆者的 Spring 在 Aop 方面的學(xué)習(xí)筆記,主要結(jié)合了 Spring In Action 第三章 和 Spring-Reference 第五章 為學(xué)習(xí)向?qū)А8鶕?jù)自己的理解和書(shū)中的實(shí)例來(lái)一步一步完成對(duì)于在 Spring 中 Aop 方面的編程。其中基礎(chǔ)部分 Ioc 需要讀者自己參考資料了解,本文將不做描述。
說(shuō)明:我將盡量縮短程序長(zhǎng)度,在程序部分將減少注釋說(shuō)明,重點(diǎn)要讀者自己根據(jù)上下文和程序結(jié)果理解體會(huì),具體 api 信息請(qǐng)讀者自己參考 Spring-api 文檔和相關(guān)資料。
一. 準(zhǔn)備工作:
1. 開(kāi)發(fā)環(huán)境:
l 適合人群:
要了解 Spring Ioc ,對(duì) Spring- Aop 可以不了解或者僅僅熟悉 Aop 概念,未參與 Spring Aop 開(kāi)發(fā)實(shí)戰(zhàn)的初學(xué)者。同時(shí)也希望高手對(duì)于本文的不足或理解錯(cuò)誤之處給予指點(diǎn),謝謝。
l 開(kāi)發(fā)環(huán)境:
JDK 1.4_2
l 開(kāi)發(fā)工具:
Eclipse 3.12 (未采用任何插件,主要是為初學(xué)者熟悉和理解 xml 文檔的配置)
l 所需組件:
Spring-Framework-1.2.8
下載地址:
2. 建立工程:
首先用 Eclpse 建立一個(gè)普通 java 項(xiàng)目,導(dǎo)入 jar 文件到編譯環(huán)境中,如下:
a) Spring.jar 為 Spring 的核心 jar 文件,必須;
b) Commons-loggin.jar 日志文件,必須;
c) Cglib.jar 動(dòng)態(tài)代理文件,不是必須(本文需要);
d) Jak-oro.jar 使用 Perl 和 Awk 正則表達(dá)式進(jìn)行文本解析工具,不是必須(本文需要);
建立工程如下:
好了,下來(lái)我們開(kāi)始我們的 Spring-aop 之旅;
二. Spring -Aop 入門(mén)
AOP 全名 Aspect-oriented programming 。 Spring framework 是很有前途的 AOP 技術(shù)。作為一種非侵略性的,輕型的 AOP framework ,你無(wú)需使用預(yù)編譯器或其他的元標(biāo)簽,便可以在 Java 程序中使用它。這意味著開(kāi)發(fā)團(tuán)隊(duì)里只需一人要對(duì)付 AOP framework ,其他人還是像往常一樣編程。
關(guān)鍵性概念:
1) Advice 是代碼的具體實(shí)現(xiàn),例如一個(gè)實(shí)現(xiàn)日志記錄的代碼。
2) Pointcut 是在將 Advice 插入到程序的條件。
3) advisor 是把 pointcut 和 advice 的組合在一起裝配器。
圖例:
你的程序可能如上,現(xiàn)在要在三個(gè)流程上同時(shí)加入日志控制和權(quán)限控制,如下:
你的程序可能如上,現(xiàn)在要在三個(gè)流程上同時(shí)加入日志控制和權(quán)限控制,如下:
其中拿日志為例,日志控制和流程之間的穿插點(diǎn)處叫做連接點(diǎn)( Joinpoint ),而 Advice 就是我們?nèi)罩咎幚淼木唧w代碼, Pointcut 就是定義一個(gè)規(guī)則,對(duì)三個(gè)和業(yè)務(wù)有關(guān)的連接點(diǎn)進(jìn)行過(guò)濾和匹配(例如我們對(duì)于業(yè)務(wù) 1 不做日志處理)。 Advisor 就是將符合的規(guī)則的剩下的兩個(gè)連接點(diǎn)和具體的日志記錄代碼組合在一起。
三. Spring-Aop 前置通知、后置通知、環(huán)繞通知、異常通知實(shí)現(xiàn)
我以 Spring In Action 提供的例子進(jìn)行二次改造,完成我們自己的流程。業(yè)務(wù)流程很簡(jiǎn)單,顧客到商店買(mǎi)東西,店員根據(jù)顧客的需要給于顧客提供服務(wù)。實(shí)現(xiàn)方法前插入,方法后插入,環(huán)繞,異常四種。
代碼:
建立一個(gè)用戶(hù)類(lèi);










三個(gè)產(chǎn)品




























建立一個(gè)接口;



















































































環(huán)繞通知的實(shí)現(xiàn),必須實(shí)現(xiàn)invoke方法,通過(guò)調(diào)用invoke.proceed()手工調(diào)用對(duì)象方法:


























前置通知的實(shí)現(xiàn);
























































自定義的異常接口;













































































沒(méi)有更多的果醬異常;


























運(yùn)行實(shí)例類(lèi);

public class RunDemo
{


public static void kwikEMart()
{

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("demo/kwikemart.xml");

//如果你想通過(guò)類(lèi)來(lái)引用這個(gè)的話(huà),就要用到CGLIB.jar了,同時(shí)在代理工廠(chǎng)里面設(shè)置:
//<property name="proxyTargetClass" value="true" />
KwikEMart akem = (KwikEMart) context.getBean("kwikEMart");

try
{
akem.buySquish(new Customer());
akem.buyPepper(new Customer());
akem.buyCheese(new Customer());

} catch (KwikEMartException e)
{
//異常已經(jīng)被截獲了,不信你看控制臺(tái)!~;
}
}


public static void main(String[] args)
{
kwikEMart();
}
}
Xml 文件配置:

































Xml 文件配置:
















































這個(gè)例子?xùn)|西很多,不過(guò)每個(gè)類(lèi)的代碼都不大。如果你對(duì) org.springframework.aop.framework.ProxyFactoryBean 不是很了解的話(huà)可以看我下篇尾處的介紹。 讀清楚之后,我們運(yùn)行RunDemo 類(lèi),查看控制臺(tái)結(jié)果,如下:
店員::Hello 悠~游! . How are you doing?
店員:悠~游! ,Can I help you ?
-- 我想買(mǎi):果醬!
店員:OK! 悠~游!.give you!
店員:Thank you 悠~游! . Come again!
店員::Hello 悠~游! . How are you doing?
店員:悠~游! ,Can I help you ?
-- 我想買(mǎi):胡椒粉!
店員:OK! 悠~游!.give you!
店員:Thank you 悠~游! . Come again!
店員::Hello 悠~游! . How are you doing?
店員:悠~游! ,Can I help you ?
-- 我想買(mǎi):奶酪!
店員:OK! 悠~游!.give you!
店員:Thank you 悠~游! . Come again!
我們將 kwikEMartTarget 里面的注釋去掉,測(cè)試異常實(shí)現(xiàn),如下:
店員::Hello 悠~游! . How are you doing?
店員:悠~游! ,Can I help you ?
系統(tǒng):NoMoreSquisheesException異常截獲了: NoMoreSquishException 異常!
好好理解一下,我就不廢話(huà)了,我們進(jìn)行下一節(jié)。
四. Spring-Aop 之Pointcut+advice+Advisor 實(shí)現(xiàn)
我們修改我們的xml文檔后如下:





















































運(yùn)行,結(jié)果如下:
店員:悠~游! ,Can I help you ?
--我想買(mǎi):果醬!
店員:OK! 悠~游!.give you!
店員:悠~游! ,Can I help you ?
--我想買(mǎi):胡椒粉!
店員:OK! 悠~游!.give you!
店員:悠~游! ,Can I help you ?
--我想買(mǎi):奶酪!
店員:OK! 悠~游!.give you!
在這里簡(jiǎn)單說(shuō)明一下xml文檔:nameMatchfilterPointcut和regexpFilterPointcut 是我們自己定義好規(guī)則的Pointcut。nameMatchfilterPointcut 根據(jù)mappedName來(lái)設(shè)置過(guò)濾規(guī)則, regexpFilterPointcut則是用pattern來(lái)設(shè)置過(guò)濾規(guī)則。runDemofilterPointcutAdvisor則將我們的Pointcut和advice組合在一起。
讀者可以自己修改runDemofilterPointcutAdvisor的pointcut來(lái)切換不同的Pointcut。如果需要RegexpMethodPointcut 的子類(lèi)的實(shí)現(xiàn),必須要oro包支持。(注:RegexpMethodPointcut有倆個(gè)子類(lèi)的實(shí)現(xiàn),JdkRegexpMethodPointcut和Perl5RegexpMethodPointcut)。
但是,如果我們想讓我們的Advisor同時(shí)實(shí)現(xiàn)多個(gè)Pointcut+advice怎么辦呢?利用Spring In Action里面的實(shí)例,我們來(lái)自己實(shí)現(xiàn)我們的Pointcut。
代碼:


















































在xml中,加入下面代碼:




















運(yùn)行的結(jié)果請(qǐng)讀者自己試驗(yàn)。這個(gè)時(shí)候您可能在想,這些pointcut都是Spring自己的實(shí)現(xiàn),我們能否自己來(lái)定義我們自己規(guī)則的pointcut呢?當(dāng)然可以!~
代碼:

/** *//**
*
* 自己定義的靜態(tài)切入點(diǎn)
* 滿(mǎn)足條件是:當(dāng)傳入的數(shù)字大于1塊錢(qián)的時(shí)候才可以;
*
* @author 悠~游
* @since 2006-06-22
* @link www.uusam.com
*/

public class MyPointcut extends StaticMethodMatcherPointcut implements Serializable
{

private static final long serialVersionUID = -101281038294508751L;

private int money = 0;


/** *//**
* 實(shí)現(xiàn)方法,業(yè)務(wù)邏輯為:根據(jù)輸入的錢(qián)數(shù)和動(dòng)作(必須是以buy開(kāi)頭的方法),來(lái)確定是否有錢(qián)購(gòu)買(mǎi)商品,買(mǎi)完之后去掉商品的價(jià)格;
*/

public boolean matches(Method method, Class targetClass)
{

if (method.getName().indexOf("buyCheese") == 0 && money >= 100)
{
money -= 100;
return true;

} else if (method.getName().indexOf("buyPepper") == 0 && money >= 5)
{
money -= 5;
return true;

} else if (method.getName().indexOf("buySquish") == 0 && money >= 10)
{
money -= 10;
return true;
}
System.out.println("門(mén)衛(wèi):你要買(mǎi)的東西太貴,你的錢(qián) "+money+" 太少!~ ,取消服務(wù)!");
return false;
}


public void setMoney(int money)
{
this.money = money;
}

}




















































這個(gè)就是我們自己定義的靜態(tài)Pointcut,主要實(shí)現(xiàn)自己的matches方法。看看如何加入到我們的XML文檔中:






很簡(jiǎn)單不是么?我們定義一個(gè)數(shù)字,就是用戶(hù)的money,進(jìn)入商店時(shí)候兜里的錢(qián)^_^。同樣修改我們的myUnionPointcut里面的pointcuts,加入我們的pointcut。










當(dāng)上面兩個(gè)Pointcut定義的規(guī)則不通過(guò)的時(shí)候,程序開(kāi)始校驗(yàn)我們的myPointcut。運(yùn)行,結(jié)果如下:
店員:悠~游! ,Can I help you ?
--我想買(mǎi):果醬!
店員:OK! 悠~游!.give you!
店員:悠~游! ,Can I help you ?
--我想買(mǎi):胡椒粉!
店員:OK! 悠~游!.give you!
門(mén)衛(wèi):你要買(mǎi)的東西太貴,你的錢(qián) 85 太少!~ , 取消服務(wù)!
--我想買(mǎi):奶酪!//服務(wù)員沒(méi)了...
好了,是不是我們想要的結(jié)果呢?呵呵。
同時(shí),Spring 提供動(dòng)態(tài)Pointcut。關(guān)于動(dòng)態(tài)的說(shuō)明我就不在熬述了,我們這里只關(guān)心具體Spring帶給我們的具體實(shí)現(xiàn)方法,具體應(yīng)用請(qǐng)讀者自己斟酌使用。














運(yùn)行,結(jié)果如下:
店員:悠~游! ,Can I help you ?
--我想買(mǎi):果醬!
店員:OK! 悠~游!.give you!
店員:悠~游! ,Can I help you ?
--我想買(mǎi):胡椒粉!
店員:OK! 悠~游!.give you!
店員:悠~游! ,Can I help you ?
--我想買(mǎi):奶酪!
店員:OK! 悠~游!.give you!
動(dòng)態(tài)切入點(diǎn)是根據(jù)當(dāng)前堆棧信息進(jìn)行方法匹配的一種規(guī)則,讀者可以自己修改demo.RunDemo,如java.lang.Integer,來(lái)看看結(jié)果。
--我想買(mǎi):果醬!
--我想買(mǎi):胡椒粉!
--我想買(mǎi):奶酪!
到這里能夠讀下來(lái)已經(jīng)很不容易了,呵呵。還是站起來(lái)走動(dòng)一下吧,接下來(lái)我們將搞定其他的一些東東。
posted on 2006-12-19 13:12 常言笑 閱讀(394) 評(píng)論(0) 編輯 收藏 所屬分類(lèi): JAVA/J2EE