?
AOP 的簡單入門
?
自己也算是從業(yè)多年,對于AOP的概念應(yīng)該算是聽的爛的不能再爛了,這方面的書也看的不少,但是自己一直沒有機會去實踐下。
乘在這個稍微有點空閑的下午,就隨手玩玩SPRING的AOP,也談?wù)勛约簩τ贏OP的理解及其衍生的一些東西。
?
1.一切術(shù)語都是紙老虎
基本概念,也可以說是基本術(shù)語。任何一個軟件概念提出時候,都少不了這個東西。CRM,AOP,SOA等等,伴隨這些東西的都會有相應(yīng)體系內(nèi)的術(shù)語。
我個人的看法是一切術(shù)語的出現(xiàn)不是并不是向大眾解釋清楚這件事到底是怎么一回事,其主要是基于兩個方面考慮:
?
1.讓自己提出觀點顯得系統(tǒng)化,更具有說服力。
2.迷惑大眾,或是迷糊那些剛進(jìn)入這個領(lǐng)域的初學(xué)者。
?
兩個看似矛盾的因素,其實歸結(jié)到本質(zhì)就是將一個簡單的東西復(fù)雜化并迷糊那些辨別能力的一般的人,大家對于抽象到一定程度的東西但是心懷敬畏之心的,然后帶著膜拜的心理去接受,生怕一不小心褻瀆了內(nèi)心的女神。扯開來講現(xiàn)在社會,官員的道德淪喪,其中的一個誘因就是對于敬畏之心的缺失,當(dāng)一個人無所畏時,才是最可怕的,因為這個時候已經(jīng)沒有任何約束能約束他的行為。
?
回歸正題,既然提到術(shù)語,那么我們就將AOP中的那些術(shù)語列出來看看。
切面(Aspect)、連接點(Joinpoint)、通知(Advice)、切入點(Pointcut) 、目標(biāo)對象(Target Object)、AOP代理(AOP Proxy)。
通知又分為幾種:前置通知(Before advice)、后通知(After advice)、返回后通知(After return advice)、環(huán)繞通知(Around advice)、拋出異常后通知(After throwing advice) 等。
?
好了,現(xiàn)在我們來看看這些術(shù)語,誰能一眼就明白這些東西能告訴我們什么?誰能通暢的理清楚它們之間的關(guān)系。開始解釋之前,我們看看維基百科上對AOP的定義是什么:
?
面向側(cè)面的程序設(shè)計(aspect-oriented programming,AOP,又譯作面向方面的程序設(shè)計、觀點導(dǎo)向編程)是計算機科學(xué)中的一個術(shù)語,指一種程序設(shè)計范型。該范型以一種稱為側(cè)面(aspect,又譯作方面)的語言構(gòu)造為基礎(chǔ),側(cè)面是一種新的模塊化機制,用來描述分散在對象、類或函數(shù)中的橫切關(guān)注點(crosscutting concern)。
?
?
多么簡單的解釋,就是對于那些在主程序業(yè)務(wù)之外的事情,我們該怎么設(shè)計,看清楚,是程序設(shè)計,而不是程序編碼,很多地方都將AOP理解成面向切面的編碼,那是錯誤,AOP約定的是一種設(shè)計范型,具體到j(luò)ava上的實現(xiàn)例如aspectJ,例如spring的AOP。再具體到實現(xiàn)技術(shù)就是JDK自帶的動態(tài)代理,cglib的字節(jié)碼修改等等。AOP != spring AOP ,AOP != cglib 。一個是設(shè)計范型,一個是實現(xiàn)。
?
(這里扯開點說,一些所謂的架構(gòu)師喜歡談概念,還有人提出架構(gòu)師更關(guān)注抽象的東西,普通的碼農(nóng)更關(guān)注具象的東西。這些東西本身沒錯,在大量的實踐之后,我們確實應(yīng)該去在這大量的實踐中歸納,總結(jié)出規(guī)律,這就是進(jìn)行抽象。但是,但是,那些只跟你扯抽象而不落地的架構(gòu)師,還是遠(yuǎn)離些,因為他們不是基于大量的具象后,進(jìn)行抽象,他們不過是邯鄲學(xué)步的抄襲那些真正的架構(gòu)師的想法,并轉(zhuǎn)變?yōu)樽约旱挠^點,TMD就是個大忽悠)
?
2.那些主程序之外的雜事
我們知道,一個程序是會被編譯成計算機能識別的機器碼,交給機器去執(zhí)行,那么我們想知道機器在執(zhí)行我們的代碼時候,發(fā)生的一些事,例如:
?
- 一個輸入是否得到我們想要的輸出呢?
- 一個函數(shù)執(zhí)行的時間開銷是多少呢?
- 若是一個良好的程序遇到無法處理的異常我們該怎么辦呢?
- 存在多個數(shù)據(jù)源,我們需要在多個數(shù)據(jù)源的更新都完成后,再提交該怎么處理呢?
- 我們想將一些我們不認(rèn)可的請求拒絕,那又該怎么處理呢?
?
當(dāng)以上的這些雜事出現(xiàn)時,我們該怎么做呢,一種很簡單粗暴的方式就是硬編碼的將這些雜事寫入到我們的主要業(yè)務(wù)程序中,我想這種方式大家在日常的開發(fā)經(jīng)常能看到,包括你去看一些所謂平臺級別的產(chǎn)品也是采用這種方式,筆者現(xiàn)在所在單位的部分產(chǎn)品也是如此。不管你爽不爽,老子爽了就可以了。
?
?
3.上吧,騷年
用個不恰當(dāng)?shù)谋扔鱽碚f:你是個開飯店的,來了很多顧客,當(dāng)你女服務(wù)員在招待顧客時,你突然發(fā)現(xiàn)XX院長來了,想來OOXX下。你就定了一個流程,來XX院長,可OOXX,硬編碼的方式就是:
1.你女服務(wù)員在招待
2.XX院長跟她OOXX
3.你女服務(wù)員在招待
4.屌絲來了,無視之
......
這個過程中你不管你女服務(wù)員是否來例假,你不關(guān)心你女服務(wù)員是否今天心情不好。嗯,院長爽了,若是你得到了某種回報,也還好,爽了;若是什么都得不到,那可就欲哭無淚。
如果你足夠幸福的話,有另一個口味美女服務(wù)員,她會隱藏技能---洗腳。恰巧經(jīng)管學(xué)院院長也來了,這個家伙還喜歡洗腳,那怎么辦,那就上吧:
1.你女服務(wù)員在招待
2.XX院長跟她OOXX+洗腳
3.你女服務(wù)員在招待
4.屌絲來了,無視之
......
如果你生意足夠好,恰好XX院長又很多,好吧,你的美女們一直處在XXOO狀態(tài)中..... 很high,很happy。但是,我們回到最開始,你為什么要招女服務(wù)員?找她們來,是因為你需要她們?nèi)フ写櫩停粋€屌絲在等吃飯沒關(guān)系,若是一群的屌絲在等吃飯,你就悲劇,沒人招待屌絲們了,因為你的那些服務(wù)員都在跟院長們OOXX中,因為命令已經(jīng)固化到流程中,你改不了,至少在你修改流程之前。通過我們軟件術(shù)語來說,就是不能及時、靈活的應(yīng)對自身內(nèi)部(你的美女們身體、心情)和外部(屌絲數(shù)量)的變化。當(dāng)然, 你若是鐵道部這樣的共和國長子,那是沒關(guān)系的,讓那群屌絲們等去吧,因為方圓960萬平方公里就此一家,別無分號。
?
若你不是,哪天覺得自己有點生意有點扛不住或是那點生殖器破事被某個黑心,吃不到葡萄的院長小弟揭發(fā)了,扛不住隨之而來的社會輿論壓力,不能跟院長們OOXX了,只準(zhǔn)對他們笑個,這個時候你得通知那些女服務(wù)員,說不準(zhǔn)OOXX了,只能看了。若是你只有一家店,還好,自己喊一聲,重新打印流程規(guī)章表,若是全國連鎖的話...... 一桌的杯具擺在你茶幾上。
??
4.正義化身的出現(xiàn)
好了,扯了這么多,終于要該AOP兄弟出場了,再不出估計戲都散場了。
針對以上的種種問題,我們該怎么處理這些我們店主要生意之外的雜事呢(OOXX),有什么更好的方式來隨時應(yīng)對種種變化。這個就是我們AOP兄弟想干的事情,從主業(yè)中剝離出這些雜事,進(jìn)行單獨處理的設(shè)計。主業(yè)務(wù)只關(guān)注于自己的領(lǐng)域,對于特殊領(lǐng)域的處理(OOXX),通過側(cè)面來封裝維護(hù),這樣使得散落在不同口味美女的特殊操作可以很好的管理起來。來段專業(yè)點的說明吧:
?
從主關(guān)注點中分離出橫切關(guān)注點是面向側(cè)面的程序設(shè)計的核心概念。分離關(guān)注點使得解決特定領(lǐng)域問題的代碼從業(yè)務(wù)邏輯中獨立出來,業(yè)務(wù)邏輯的代碼中不再含有針對特定領(lǐng)域問題代碼的調(diào)用,業(yè)務(wù)邏輯同特定領(lǐng)域問題的關(guān)系通過側(cè)面來封裝、維護(hù),這樣原本分散在在整個應(yīng)用程序中的變動就可以很好的管理起來。
?
?
好了,AOP的就是個這么簡單的東西,別去想那些繁雜的spring配置和概念術(shù)語。它只是一種設(shè)計范型。
?
?
繞了這么久,讓我們來打倒那些紙老虎吧。
我開飯店,屌絲、院長來吃飯,美女們招待顧客 ,這個是我們的主業(yè)。 ?======== ?目標(biāo)對象(Target Object) 就是店主我,我開了兩個店,戲院和飯店
哦,北大院長來飯店吃飯了 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?======== ?切入點(Pointcut) 他們來我戲院看戲的話,不管,直管飯店的事
院長開始吃飯,喝酒了。 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ======== ?連接點(Joinpoint) ,就是我們的一些行為,院長如果來圍觀的話,無視之,哥是開飯店的。
院長想跟美女們OOXX了 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ======== ?通知(Advice)院長來了,也吃了飯了,那接下來干什么呢?通知就是決定干什么:OOXX或是洗腳
院長除了想OOXX之外,還想洗腳,那么怎么辦呢? ? ? ? ? ? ? ? ? ? ? ? ? ? ======== ?切面(Aspect) ,規(guī)定院長來了可以干什么,就是決定可以有多少個通知:OOXX||洗腳 或是 OOXX && 洗腳
----------------------------------------------------------------------------------------------------
院長想吃飯后洗腳 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?======== 后通知(After advice)?
院長想吃飯前洗腳 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?======== 前置通知(Before advice)
院長想根據(jù)吃飯后的心情決定是OOXX還是洗腳 ? ? ? ? ? ? ? ? ? ? ? ? ======== 返回后通知(After return advice)
院長吃飯吃出腦中風(fēng)了 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?======== 拋出異常后通知(After throwing advice)這個時候有個通知跳出來:打120,送醫(yī)院!
院長想飯前洗腳,飯后OOXX ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ======== 環(huán)繞通知(Around advice)
?
作為老板的我,應(yīng)該怎么更好的切入這些洗腳啊,OOXX服務(wù)呢 ? ?======== AOP代理(AOP Proxy)怎么在干好招待顧客這件事上切入 洗腳||OOXX
?
若是上面的這些你還是看不明白的話,那么我們就具象到spring上,看看到底是件上面事吧。spring中Aspect叫Advisor。Joinpoint叫Weaving。很操蛋,也很讓人無語的術(shù)語啊
?
?
切面:
package com.zhaming.aop.advice;
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
/**
* 后置通知
* @author inter12
*
*/
public class AfterAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("攔截了:" + method.getName() + "方法");
System.out.println("洗腳");
}
}
package com.zhaming.aop.advice;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
/**
* 前置通知
*
* @author inter12
*/
public class BeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("攔截了:" + method.getName() + "方法");
System.out.println("OOXX");
}
}
package com.zhaming.aop.advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
* 環(huán)繞通知
*
* @author inter12
*/
public class CompareAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Object result = null;
String userName = invocation.getArguments()[0].toString();
if (null != userName && userName.equals("yuanzhang")) {
System.out.println("院長通過---------------");
result = invocation.proceed();
} else {
System.out.println("屌絲拒絕---------------");
}
return result;
}
}
目標(biāo)對象:
package com.zhaming.aop.restaurant;
public interface RestaurantService {
public void zhaodaiguke(String userName);
public void weiguan(String userName);
}
package com.zhaming.aop.restaurant;
/**
* 目標(biāo)對象
*
* @author inter12
*/
public class RestaurantServiceImpl implements RestaurantService {
@Override
public void zhaodaiguke(String userName) {
System.out.println("--------- 姑娘們在招待顧客:" + userName);
}
@Override
public void weiguan(String userName) {
System.out.println(userName + ":在圍觀");
}
}
客戶端:
package com.zhaming.aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import com.zhaming.aop.restaurant.RestaurantService;
public class Main {
public static void main(String[] args) {
ApplicationContext applicationContext = new FileSystemXmlApplicationContext(
"http://home/inter12/workspace/Light/src/main/java/appcontext-aop.xml");
RestaurantService bean = (RestaurantService) applicationContext.getBean("restaurantService");
bean.zhaodaiguke("yuanzhang");
bean.zhaodaiguke("diaosi");
}
}
配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<!-- 通知 -->
<bean id="beforeAdvice" class="com.zhaming.aop.advice.BeforeAdvice"></bean>
<bean id="afterAdvice" class="com.zhaming.aop.advice.AfterAdvice"></bean>
<bean id="compareAdvice" class="com.zhaming.aop.advice.CompareAdvice"></bean>
<!-- 目標(biāo)對象 -->
<bean id="restaurantServiceTarget" class="com.zhaming.aop.restaurant.RestaurantServiceImpl"></bean>
<bean id="restaurantService" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 攔截那些接口 : 切入點 只關(guān)心飯店的事-->
<property name="proxyInterfaces">
<value>com.zhaming.aop.restaurant.RestaurantService</value>
</property>
<!-- 對這些方式做那些攔截:切面 -->
<property name="interceptorNames">
<list>
<!--
<value>beforeAdvice</value>
<value>afterAdvice</value>
-->
<value>compareAdvice</value>
</list>
</property>
<property name="target">
<ref bean="restaurantServiceTarget" />
</property>
</bean>
</beans>
?
?
?
5.AOP能干什么?
現(xiàn)在回頭看看最初問的五個問題,那些雜事,是不是可以對應(yīng)到軟件中的幾個概念:日志記錄,性能統(tǒng)計,安全控制,事務(wù)處理,異常處理,更衍生的提還有緩存,持久化,同步等
已有 19 人發(fā)表留言,猛擊->>這里<<-參與討論
ITeye推薦