spring 實現aop的三種方法

 

 spring開發aop應用有三種方法:

一:Spring 1.2版本中通過ProxyFactoryBean來實現aop,即通過動態代理來實現的,Aspect必須繼承MethodBeforeAdvice,MethodAfterAdvice等

<!--被代理的對象-->

<bean id="man" class="Man">
<property name="name">
<value type="java.lang.String">張三</value>
</property>
</bean>

<!--繼承了MethodBeforeAdvice類的 Aspect->
<bean id="fbi" class="FBI" />

<bean id="civilian"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target">
<ref bean="man" />
</property>

<property name="interceptorNames">
<list>
<value>fbi</value>
</list>
</property>
</bean>

 

二:Spring 2.0 AOP 應用

需要改的是FBI 這個類,而且它也不需要再實現某些接口了

public class FBI {
public void before(JoinPoint point){
Man man = (Man)point.getTarget();
System.err.println("FBI 發現" + man.getName() + "正在進行 " +
point.getSignature().getName() + " 活動。");
}
}

注意這個類裏面的方法 before(JoinPoint),方法名可以是任意的,可以帶一個JoinPoint 類
型的參數,也可以不帶參數直接寫成before(),但是這個連接點(JoinPoint)對象帶來了所
有和這次方法調用有關的信息,包括方法參數,目標對象等等,所以一般要做日誌記錄的話
會帶上它。
接下來是測試類的代碼,和以前的幾乎沒有任何不同,只不過現在直接訪問的是man
這個bean。

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="
http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="
http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
<bean id="fbi" class="FBI" />
<bean id="man" class="Man">
<property name="name">
<value type="java.lang.String">張三</value>
</property>
</bean>
<aop:config>
<aop:pointcut id="manPointcut"
expression="execution(* Man.*(..))" />
<aop:aspect id="beforeExample" ref="fbi">
<aop:before pointcut-ref="manPointcut" method="before" />
</aop:aspect>
</aop:config>
</beans>

1. 配置文件的開頭加入了aop 命名空間,如代碼中粗斜體所示。
2. 使用aop:config 標籤來定義AOP,不是使用ProxyFactoryBean 來定義一個新的
bean。

一個是人的對象,另
一個則是聯邦調查局的探員。而aop:config 中定義了所有的AOP 設置信息。aop:pointcut
定義了一個切入點,id 給出了這個切入點的唯一名字,而expression 定義了切入點的表達
式,那麼這個定義到底表示了什麼信息呢?它的意思是表示一種場景,即執行(execution)
Man 對象的所有方法的這種情況,這就是表達式execution(* Man.*(..))的意義所在,
Man.*(..)表示Man 類的所有方法。接下來呢,需要定義一個切面,用aop:aspect 來定義,
它的ref 屬性指定了這個切面所對應的bean 定義的id,這裏指向fbi 這個bean 類;子標籤
aop:before 則指示了當發生了名爲manPointcut 的切入點(情況)前(用pointcut-ref 屬性
指定,pointcut-ref=”manPointcut”),就調用名爲before 的方法,這個方法位於aspect 裏
面的引用的那個bean 中,這裏是fbi(即ref=”fbi”)。其實Spring 執行到這裏後,會自動的
把這些代碼翻譯成底層的Bean 定義(後臺依然會採用ProxyFactoryBean 這樣的機制),
然後把對應的獲取bean 的操作直接委託給代理類,這就是爲什麼上文提到的測試類只需要
訪問原來的man 這個bean,對應的攔截類就會被執行的原因。從這裏看到Spring 2.0 中要
定義一個AOP 的bean 類,仍然是比較複雜的,XML 文件和概念都增加了很多,需要讀者
慢慢來學習和理解。

三使用標註(@AspectJ)實現AOP

的一個庫來做切點(pointcut)解析和匹配。
爲了在Spring 配置中使用@AspectJ aspects,你必須首先啓用Spring 對基於@AspectJ
aspects 的配置支持,自動代理(autoproxying)基於通知是否來自這些切面。 自動代理是
指Spring 會判斷一個bean 是否使用了一個或多個切面通知,並據此自動生成相應的代理
以攔截其方法調用,並且確認通知是否如期進行。
通過在你的Spring 的配置文件中引入下列元素來啓用Spring 對@AspectJ 的支持:
<aop:aspectj-autoproxy/>
也可以通過在你的application context 中添加如下定義來啓用@AspectJ 支持:
<bean
class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyC
reator" />
你需要在你的應用程序的classpath 中引入兩個AspectJ 庫:aspectjweaver.jar 和
aspectjrt.jar。我們這裏用的MyEclipse,在添加Spring 開發功能時已經自動的加入了這些
類庫文件,無需手工配置了。
定義切面Aspect:在啓用@AspectJ 支持的情況下,在application context 中定義的任意帶有一個@Aspect 切面(擁有@Aspect 標註)的bean 都將被Spring 自動識別並用於
配置在Spring AOP。
定義切入點Pointcut:現在通過在 @AspectJ 標註風格的 AOP 中,一個切入點簽名
通過一個普通的方法定義來提供,並且切入點表達式使用 @Pointcut 標註來表示(作爲切
入點簽名的方法必須返回 void 類型)。代碼可以參考清單10.12。
好了,引用了這麼些文檔,我們需要介紹這個基於標註的新的AOP項目了,這個項目
的名字是Spring2_0AOPAspectJ,如前一節所示加入了Spring核心和AOP類庫後,就可以
開發了。那麼相比較10.4.1 使用aop 標籤實現AOP一節,這一個項目的代碼僅僅有兩個地
方要改。首先我們要修改FBI類的源碼,加入標註來實現切面和切入點定義,如下所示:
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
/**
* 聯邦調查局的探員將您的所有行動都記錄在案。
* @author BeanSoft
*/
@Aspect
public class FBI {
@Before("execution(* Man.*(..))")
public void before(JoinPoint point){
Man man = (Man)point.getTarget();
System.err.println("FBI 發現" + man.getName() + "正在進行 " +
point.getSignature().getName() + " 活動。");
}
}
清單10.12 加入了Aspect 標註的FBI 類
這個類中的@Before 後面的"execution(* Man.*(..))"是切入點所對應的切入點點表達式,其意
義和上一節的是一致的,仍然表示的是執行 Man 類的所有方法時將觸發此方法的執行。
使用了這種寫法後,XML 配置文件將大大簡化,其內容如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="
http://www.springframework.org/schema/beans"
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.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
<aop:aspectj-autoproxy/><bean id="fbi" class="FBI" />
<bean id="man" class="Man">
<property name="name">
<value type="java.lang.String">張三</value>
</property>
</bean>
</beans>

1. 加入了粗斜體的<aop:aspectj-autoproxy/>定義;
2. 去掉了<aop:config>標籤部分。
可以看到使用這種方式後,AOP 的開發和配置變的極其簡單。這就是JDK 1.5 引入標註開
發後帶來的好處。當然弱點嘛,那就是要修改配置必須重新編譯源代碼了。
注意:在這裏你不能去掉<bean id="fbi" class="FBI" />這一個bean的定義,否
則自動AOP代理對象就沒有機會被創建並工作了,那樣的話man對象被代理也就無從談起
了。

四開發環繞通知(Around Advice)AOP 應用

@Aspect
public class FBI {
@Around("execution(* Man.*(..))")
public Object before(ProceedingJoinPoint point) throws Throwable {
Man man = (Man)point.getTarget();
System.err.println("FBI 發現" + man.getName() + "即將正在進行 " +
point.getSignature().getName() + " 活動。");
// 禁止張三泡MM
if(point.getSignature().getName().equals("mm")) {
System.err.println("FBI 將阻止 " + man.getName() + " 泡MM。");
} else if(point.getSignature().getName().equals("sayHelp")) {
System.err.println("FBI 將欺騙 " + man.getName() + " 的朋友告
訴他們他很好。");
return "我是 " + man.getName() + " ,我現在過的很好。";
} else {Object object = point.proceed();
System.err.println("FBI 發現" + man.getName() + "已經完成了 " +
point.getSignature().getName() + " 活動。");
return object;
}
return null;
}
}

現在張三不光是不能泡MM 了,當他求救的時候,FBI 還可以直接攔截並修改,將其請求的
信息“救我,我是張三!”改成“我是張三,我現在過的很好。”,這樣通過欺騙行爲,張三
的朋友永遠也不知道發生了什麼事。

/**
* 具有聊QQ和泡MM以及求救三個行爲的人對象,還有一個用戶名屬性。
* @author BeanSoft
*/
public class Man {
private String name;
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
public void qq() {
System.out.println("我在聊QQ");
}
public void mm() {
System.out.println("我在泡MM");
}
public String sayHelp() {
return "救我,我是" + getName();
}
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章