?

AOP 的簡單入門

?

自己也算是從業多年,對于AOP的概念應該算是聽的爛的不能再爛了,這方面的書也看的不少,但是自己一直沒有機會去實踐下。

乘在這個稍微有點空閑的下午,就隨手玩玩SPRING的AOP,也談談自己對于AOP的理解及其衍生的一些東西。

?

1.一切術語都是紙老虎

基本概念,也可以說是基本術語。任何一個軟件概念提出時候,都少不了這個東西。CRM,AOP,SOA等等,伴隨這些東西的都會有相應體系內的術語。

我個人的看法是一切術語的出現不是并不是向大眾解釋清楚這件事到底是怎么一回事,其主要是基于兩個方面考慮:

?

1.讓自己提出觀點顯得系統化,更具有說服力。

2.迷惑大眾,或是迷糊那些剛進入這個領域的初學者。

?

兩個看似矛盾的因素,其實歸結到本質就是將一個簡單的東西復雜化并迷糊那些辨別能力的一般的人,大家對于抽象到一定程度的東西但是心懷敬畏之心的,然后帶著膜拜的心理去接受,生怕一不小心褻瀆了內心的女神。扯開來講現在社會,官員的道德淪喪,其中的一個誘因就是對于敬畏之心的缺失,當一個人無所畏時,才是最可怕的,因為這個時候已經沒有任何約束能約束他的行為。

?

回歸正題,既然提到術語,那么我們就將AOP中的那些術語列出來看看。

切面(Aspect)、連接點(Joinpoint)、通知(Advice)、切入點(Pointcut) 、目標對象(Target Object)、AOP代理(AOP Proxy)。

通知又分為幾種:前置通知(Before advice)、后通知(After advice)、返回后通知(After return advice)、環繞通知(Around advice)、拋出異常后通知(After throwing advice) 等。

?

好了,現在我們來看看這些術語,誰能一眼就明白這些東西能告訴我們什么?誰能通暢的理清楚它們之間的關系。開始解釋之前,我們看看維基百科上對AOP的定義是什么:

?

面向側面的程序設計(aspect-oriented programming,AOP,又譯作面向方面的程序設計、觀點導向編程)是計算機科學中的一個術語,指一種程序設計范型。該范型以一種稱為側面(aspect,又譯作方面)的語言構造為基礎,側面是一種新的模塊化機制,用來描述分散在對象、類或函數中的橫切關注點(crosscutting concern)。
?

?

多么簡單的解釋,就是對于那些在主程序業務之外的事情,我們該怎么設計,看清楚,是程序設計,而不是程序編碼,很多地方都將AOP理解成面向切面的編碼,那是錯誤,AOP約定的是一種設計范型,具體到java上的實現例如aspectJ,例如spring的AOP。再具體到實現技術就是JDK自帶的動態代理,cglib的字節碼修改等等。AOP != spring AOP ,AOP != cglib 。一個是設計范型,一個是實現。

?

(這里扯開點說,一些所謂的架構師喜歡談概念,還有人提出架構師更關注抽象的東西,普通的碼農更關注具象的東西。這些東西本身沒錯,在大量的實踐之后,我們確實應該去在這大量的實踐中歸納,總結出規律,這就是進行抽象。但是,但是,那些只跟你扯抽象而不落地的架構師,還是遠離些,因為他們不是基于大量的具象后,進行抽象,他們不過是邯鄲學步的抄襲那些真正的架構師的想法,并轉變為自己的觀點,TMD就是個大忽悠)

?

2.那些主程序之外的雜事

我們知道,一個程序是會被編譯成計算機能識別的機器碼,交給機器去執行,那么我們想知道機器在執行我們的代碼時候,發生的一些事,例如:

?

  1. 一個輸入是否得到我們想要的輸出呢?
  2. 一個函數執行的時間開銷是多少呢?
  3. 若是一個良好的程序遇到無法處理的異常我們該怎么辦呢?
  4. 存在多個數據源,我們需要在多個數據源的更新都完成后,再提交該怎么處理呢?
  5. 我們想將一些我們不認可的請求拒絕,那又該怎么處理呢?

?

當以上的這些雜事出現時,我們該怎么做呢,一種很簡單粗暴的方式就是硬編碼的將這些雜事寫入到我們的主要業務程序中,我想這種方式大家在日常的開發經常能看到,包括你去看一些所謂平臺級別的產品也是采用這種方式,筆者現在所在單位的部分產品也是如此。不管你爽不爽,老子爽了就可以了。

?

?

3.上吧,騷年

用個不恰當的比喻來說:你是個開飯店的,來了很多顧客,當你女服務員在招待顧客時,你突然發現XX院長來了,想來OOXX下。你就定了一個流程,來XX院長,可OOXX,硬編碼的方式就是:

1.你女服務員在招待

2.XX院長跟她OOXX

3.你女服務員在招待

4.屌絲來了,無視之

......

這個過程中你不管你女服務員是否來例假,你不關心你女服務員是否今天心情不好。嗯,院長爽了,若是你得到了某種回報,也還好,爽了;若是什么都得不到,那可就欲哭無淚。

如果你足夠幸福的話,有另一個口味美女服務員,她會隱藏技能---洗腳。恰巧經管學院院長也來了,這個家伙還喜歡洗腳,那怎么辦,那就上吧:

1.你女服務員在招待

2.XX院長跟她OOXX+洗腳

3.你女服務員在招待

4.屌絲來了,無視之

......

如果你生意足夠好,恰好XX院長又很多,好吧,你的美女們一直處在XXOO狀態中..... 很high,很happy。但是,我們回到最開始,你為什么要招女服務員?找她們來,是因為你需要她們去招待顧客,一個屌絲在等吃飯沒關系,若是一群的屌絲在等吃飯,你就悲劇,沒人招待屌絲們了,因為你的那些服務員都在跟院長們OOXX中,因為命令已經固化到流程中,你改不了,至少在你修改流程之前。通過我們軟件術語來說,就是不能及時、靈活的應對自身內部(你的美女們身體、心情)和外部(屌絲數量)的變化。當然, 你若是鐵道部這樣的共和國長子,那是沒關系的,讓那群屌絲們等去吧,因為方圓960萬平方公里就此一家,別無分號。

?

若你不是,哪天覺得自己有點生意有點扛不住或是那點生殖器破事被某個黑心,吃不到葡萄的院長小弟揭發了,扛不住隨之而來的社會輿論壓力,不能跟院長們OOXX了,只準對他們笑個,這個時候你得通知那些女服務員,說不準OOXX了,只能看了。若是你只有一家店,還好,自己喊一聲,重新打印流程規章表,若是全國連鎖的話...... 一桌的杯具擺在你茶幾上。

??

4.正義化身的出現

好了,扯了這么多,終于要該AOP兄弟出場了,再不出估計戲都散場了。

針對以上的種種問題,我們該怎么處理這些我們店主要生意之外的雜事呢(OOXX),有什么更好的方式來隨時應對種種變化。這個就是我們AOP兄弟想干的事情,從主業中剝離出這些雜事,進行單獨處理的設計。主業務只關注于自己的領域,對于特殊領域的處理(OOXX),通過側面來封裝維護,這樣使得散落在不同口味美女的特殊操作可以很好的管理起來。來段專業點的說明吧:

?

從主關注點中分離出橫切關注點是面向側面的程序設計的核心概念。分離關注點使得解決特定領域問題的代碼從業務邏輯中獨立出來,業務邏輯的代碼中不再含有針對特定領域問題代碼的調用,業務邏輯同特定領域問題的關系通過側面來封裝、維護,這樣原本分散在在整個應用程序中的變動就可以很好的管理起來。
?

?

好了,AOP的就是個這么簡單的東西,別去想那些繁雜的spring配置和概念術語。它只是一種設計范型。

?

?

繞了這么久,讓我們來打倒那些紙老虎吧。

我開飯店,屌絲、院長來吃飯,美女們招待顧客 ,這個是我們的主業。 ?======== ?目標對象(Target Object) 就是店主我,我開了兩個店,戲院和飯店

哦,北大院長來飯店吃飯了 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?======== ?切入點(Pointcut) 他們來我戲院看戲的話,不管,直管飯店的事

院長開始吃飯,喝酒了。 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ======== ?連接點(Joinpoint) ,就是我們的一些行為,院長如果來圍觀的話,無視之,哥是開飯店的。

院長想跟美女們OOXX了 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ======== ?通知(Advice)院長來了,也吃了飯了,那接下來干什么呢?通知就是決定干什么:OOXX或是洗腳

院長除了想OOXX之外,還想洗腳,那么怎么辦呢? ? ? ? ? ? ? ? ? ? ? ? ? ? ======== ?切面(Aspect) ,規定院長來了可以干什么,就是決定可以有多少個通知:OOXX||洗腳 或是 OOXX && 洗腳

----------------------------------------------------------------------------------------------------

院長想吃飯后洗腳 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?======== 后通知(After advice)?

院長想吃飯前洗腳 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?======== 前置通知(Before advice)

院長想根據吃飯后的心情決定是OOXX還是洗腳 ? ? ? ? ? ? ? ? ? ? ? ? ======== 返回后通知(After return advice)

院長吃飯吃出腦中風了 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?======== 拋出異常后通知(After throwing advice)這個時候有個通知跳出來:打120,送醫院!

院長想飯前洗腳,飯后OOXX ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ======== 環繞通知(Around advice)

?

作為老板的我,應該怎么更好的切入這些洗腳啊,OOXX服務呢 ? ?======== AOP代理(AOP Proxy)怎么在干好招待顧客這件事上切入 洗腳||OOXX

?

若是上面的這些你還是看不明白的話,那么我們就具象到spring上,看看到底是件上面事吧。spring中Aspect叫Advisor。Joinpoint叫Weaving。很操蛋,也很讓人無語的術語啊

?

?

切面:
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;

/**
 * 環繞通知
 * 
 * @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;
    }

}

目標對象:
package com.zhaming.aop.restaurant;

public interface RestaurantService {

    public void zhaodaiguke(String userName);

    public void weiguan(String userName);
}

package com.zhaming.aop.restaurant;

/**
 * 目標對象
 * 
 * @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>

	<!-- 目標對象 -->
	<bean id="restaurantServiceTarget" class="com.zhaming.aop.restaurant.RestaurantServiceImpl"></bean>

	<bean id="restaurantService" class="org.springframework.aop.framework.ProxyFactoryBean">

		<!-- 攔截那些接口 : 切入點 只關心飯店的事-->
		<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能干什么?

現在回頭看看最初問的五個問題,那些雜事,是不是可以對應到軟件中的幾個概念:日志記錄,性能統計,安全控制,事務處理,異常處理,更衍生的提還有緩存,持久化,同步等



已有 19 人發表留言,猛擊->>這里<<-參與討論


ITeye推薦