带有命名切入点的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 就会自动绑定它。该对象将包含您通过上下文绑定可以获得的所有参数、返回值等。但是,在处理该上下文信息时,您真正得到的是 ObjectsObject[] 项。您仍然需要进行类型转换并处理潜在的异常。通过上下文绑定,您只获得您正在寻找的内容(无需在 JoinPoint 数据结构中进行探究),并且该数据是强类型的。代码更少 == 维护更少 == 成本更低!(但这正是 AOP 的宗旨,不是吗?)

对于有兴趣的人,我将在此示例中的源代码 放在这里。

获取 Spring 新闻通讯

通过 Spring 新闻通讯保持联系

订阅

领先一步

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

了解更多

获得支持

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件,只需一份简单的订阅。

了解更多

即将举行的活动

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

查看所有