使用具名 Pointcut 的 AOP 環境綁定

工程 | Ben Hale | 2007 年 3 月 29 日 | ...

Spring AOP 中有大量新功能,包括 AspectJ pointcut 語言、<aop:*/> 命名空間和 @AspectJ 語法支援。 但到目前為止,最強大的方面之一(請原諒這個雙關語)是 AOP 環境綁定。

例如,假設您想要建議一個以 String 作為引數的方法。


public interface HelloService {
	String getHelloMessage(String toAddHello);
}

為了建議這個方法,您需要撰寫一個 pointcut,尋找 String 回傳型別、HelloService 介面的所有實作和 getHelloMessage(String) 方法。


@Before("execution(public java.lang.String aop.HelloService+.getHelloMessage(String))")

但如果您想要套用一個需要在內部使用該 String 引數的 advice 呢?


public void filter(String input) {
	if (obscenities.contains(input)) {
		throw new IllegalArgumentException("Obscenities such as '" + input + "' will not be tolerated!");
	}
}

這就是 AOP 環境綁定發揮作用的地方。 原來 Spring 支援相當多的 AspectJ pointcut 語言。 此處與我們相關的部分是 args() 運算子。 這個運算子允許我們選取一個引數,並將該引數繫結到我們的 advice。 因此,當 pointcut 和 advice 結合時,您會看到如下所示的內容。


@Before("execution(public java.lang.String aop.HelloService+.getHelloMessage(String)) && args(input)")
public void filter(String input) {
	if (obscenities.contains(input)) {
		throw new IllegalArgumentException("Obscenities such as '" + input + "' will not be tolerated!");
	}
}

這非常酷。 您可以從 advised 方法到 advice 獲得引數的強型別和具名繫結。 在更複雜的範例中,也可以將多個環境資訊片段(例如其他引數、正在呼叫的物件等)繫結到 advice。 我要指出的一件事是,它一直困擾著我,args() 運算子中的引數名稱對應於advice 方法中引數的名稱。

此特定組態的問題在於,大多數時候您不會建立具有內嵌 pointcut 的 advice 宣告。 通常您會將 pointcut 外部化為具名 pointcut。 為此,您需要引入另一個間接層級。 也就是說,具名 pointcut 後面接著 advice 定義。


@Pointcut("execution(public java.lang.String aop.HelloService+.getHelloMessage(String)) && args(helloInput)")
public void helloService(String helloInput) {}


@Before("helloService(input)")
public void filter(String input) {
	if (obscenities.contains(input)) {
		throw new IllegalArgumentException("Obscenities such as '" + input + "' will not be tolerated!");
	}
}

在此範例中,需要注意的重要一點是,具名 pointcut 必須將環境資訊的類型作為引數,以便將該引數傳播到 advice。 您可以看到 helloService(String) 採用 String,以便 Before advice 可以參考 helloService(input)

最後一步是建立一個將系統連接在一起的組態檔。


<?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:util="http://www.springframework.org/schema/util"
	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
		http://www.springframework.org/schema/util
		http://www.springframework.org/schema/util/spring-util-2.0.xsd">

	<bean id="helloService" class="aop.HelloServiceImpl" />

	<bean id="obscenityFilter" class="aop.ObscenityFilter">
		<constructor-arg>
			<util:set>
				<value>Microsoft</value>
			</util:set>
		</constructor-arg>
	</bean>

	<aop:aspectj-autoproxy>
		<aop:include name="obscenityFilter" />
	</aop:aspectj-autoproxy>

</beans>


現在,有些人可能會問,為什麼要使用 AOP 環境綁定。 確實,您可以簡單地將 JoinPoint 引數新增到 advice,Spring AOP 會自動繫結它。 該物件將包含您可以使用環境綁定獲得的所有引數回傳值等。 但是,在處理該環境資訊時,您真正獲得的只是 ObjectsObject[] 項目。 您最終還是會進行轉換並處理潛在的例外狀況。 透過環境繫結,您只會獲得您正在尋找的東西(無需在 JoinPoint 資料結構中尋找),而且該資料是強型別的。 更少的程式碼 == 更少的維護 == 更低的成本! (但這就是 AOP 的全部意義,對吧?)

對於那些有興趣的人,我將這個範例的原始程式碼放在這裡 here.

取得 Spring 電子報

隨時掌握 Spring 電子報

訂閱

搶先一步

VMware 提供培訓和認證,以加速您的進度。

了解更多

取得支援

Tanzu Spring 在一個簡單的訂閱中提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位檔案。

了解更多

即將舉辦的活動

查看 Spring 社群中所有即將舉辦的活動。

查看全部