使用命名切入点的 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 社区中所有即将举行的活动。

查看全部