使用命名切入點的 AOP 上下文繫結

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

Spring AOP 中有很多新功能,包括 AspectJ 切入點語言、<aop:*/> 名稱空間和 @AspectJ 語法支援。 但到目前為止,最強大的方面(請原諒這個雙關語)之一是 AOP 上下文繫結。

例如,假設您想通知一個將 String 作為引數的方法。


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

要通知此方法,您需要編寫一個切入點,該切入點查詢 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 切入點語言。 這裡我們關心的部分是 args() 運算子。 此運算子允許我們選擇一個引數並將該引數繫結到我們的 advice。 因此,當切入點和 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!");
	}
}

現在這非常酷。 您可以從通知的方法中獲得對引數的強型別和命名繫結到 advice 中。 在更復雜的示例中,可以繫結多個上下文片段,例如其他引數、正在呼叫的物件以及更多內容到 advice。 我想指出一件事,因為它總讓我絆倒,那就是 args() 運算子中的引數名稱對應於advice 方法中的引數名稱。

此特定配置的問題在於,大多數時候您不會建立帶有嵌入式切入點的 advice 宣告。 通常,您會將切入點外部化為命名切入點。 為此,您需要引入另一個級別的間接定址。 也就是說,命名切入點後跟 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!");
	}
}

在此示例中需要注意的重要事項是,命名切入點需要將上下文型別作為引數,以便可以將該引數傳播到 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 的全部意義,對吧?)

對於那些感興趣的人,我在此示例中提供了原始碼 就在這裡。

獲取 Spring 新聞郵件

透過 Spring 新聞郵件保持聯絡

訂閱

搶佔先機

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

瞭解更多

獲得支援

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

瞭解更多

即將到來的活動

檢視 Spring 社群中所有即將舉行的活動。

檢視全部