Get ahead
VMware offers training and certification to turbo-charge your progress.
Learn moreThere a a ton of new features in Spring AOP including the AspectJ pointcut language, the <aop:*/> namespace, and the @AspectJ syntax support. But by far one of the most powerful aspects (forgive the pun) is the AOP context binding.
For example, let's say you want to advise a method that takes a String as an argument.
public interface HelloService {
String getHelloMessage(String toAddHello);
}
To advise this method, you'd write a pointcut that looked for a String return type, all implementations of the HelloService interface and the getHelloMessage(String) method.
@Before("execution(public java.lang.String aop.HelloService+.getHelloMessage(String))")
But what if you wanted to apply an advice that needed that String argument internally?
public void filter(String input) {
if (obscenities.contains(input)) {
throw new IllegalArgumentException("Obscenities such as '" + input + "' will not be tolerated!");
}
}
Well that's where AOP context binding comes into play. It turns out that Spring supports quite a bit of the AspectJ pointcut language. That part that concerns us here is the args() operator. This operator can allow us to select an argument and bind that argument to our advice. So when the pointcut and advice are combined, this is what you'd see.
@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!");
}
}
Now this is pretty cool. You get strongly typed and named binding of an argument from the advised method into the advice. In the more complex example, it's possible to bind multiple pieces of context such as other arguments, the object being invoked and more to the advice as well. One thing I do want to point out because it trips me up all the time is that the name of the argument in the args() operator corresponds to the name of the argument in the advice method.
The problem with this particular configuration is that most of the time you don't create advice declarations with embedded pointcuts. Typically you'd externalize the pointcut to a named pointcut. To do that, you need to introduce another level of indirection. To wit, the named pointcut followed by the advice definition.
@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!");
}
}
The important thing to note in this example is that the named pointcut is required to take as an argument the type of context so that argument can be propagated to the advice. You see the helloService(String) taking a String so that the Before advice can reference helloService(input).
The final step is to create an configuration file that glues the system together.
<?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>
For those interested I've got the source code in this example right here.