使用命名切入点的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`参数的建议怎么办?


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

这就是 AOP 上下文绑定发挥作用的地方。事实证明,Spring 支持相当多的 AspectJ 切入点语言。我们这里关注的部分是`args()`运算符。此运算符允许我们选择一个参数并将该参数绑定到我们的建议。因此,当切入点和建议组合在一起时,您将看到以下内容。


@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!");
	}
}

现在这很酷。您可以将来自被建议方法的参数强类型并命名地绑定到建议中。在更复杂的示例中,还可以将多个上下文片段(例如其他参数、被调用的对象以及更多内容)绑定到建议中。我想指出的一件事是因为它一直让我很困扰,那就是`args()`运算符中参数的名称与**建议方法**中参数的名称相对应。

此特定配置的问题在于,大多数情况下,您不会创建具有嵌入式切入点的建议声明。通常,您会将切入点外部化到命名切入点。为此,您需要引入另一层间接寻址。也就是说,命名切入点后跟建议定义。


@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!");
	}
}

在此示例中需要注意的重要一点是,命名切入点需要将上下文类型作为参数,以便该参数可以传播到建议。您会看到`helloService(String)`接受一个`String`,以便 Before 建议可以引用`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`参数,Spring AOP 将自动将其绑定。该对象将包含您可以使用上下文绑定获得的所有参数返回值等。但是,当处理上下文信息时,您真正获得的只是`Objects`和`Object[]`项。您仍然最终会进行强制转换并处理潜在的异常。使用上下文绑定,您只获得所需的内容(无需在 JoinPoint 数据结构中四处查找),并且该数据是强类型的。代码越少==维护越少==成本越低!(但这本来就是 AOP 的目的,对吧?)

对于感兴趣的人,我在这个例子中提供了源代码在这里。

获取Spring简讯

通过Spring简讯保持联系

订阅

领先一步

VMware 提供培训和认证,以加速您的进步。

了解更多

获得支持

Tanzu Spring在一个简单的订阅中提供对OpenJDK™、Spring和Apache Tomcat®的支持和二进制文件。

了解更多

即将举行的活动

查看Spring社区中所有即将举行的活动。

查看全部