This is a fun and elegant method. Even though this article is called "AOP 101", I'm not planning to explain the concepts or weird terminology; rather I just want to show you the code and assume that you'll be able to see what's happening.
First we need to create an "advice" class. This is the code that we're going
to wrap around our send() invocations. Listing 3 shows the code.
package app.aop;
import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
public class ForkAdvice {
private static final Logger log = Logger.getLogger(ForkAdvice.class);
public void fork(final ProceedingJoinPoint pjp) {
new Thread(new Runnable() {
public void run() {
log.info("Forking method execution: " + pjp);
try {
pjp.proceed();
} catch (Throwable t) {
// All we can do is log the error.
log.error(t);
}
}
}).start();
}
}
The fork method is "around" advice that we're going to use to advise our
calls to JavaMailSenderImpl.send(). As you can see, it creates
a new thread and starts it. In the run() body, we simply execute
the advised method by calling pjp.proceed().
As an aside, the ProceedingJoinPoint class is provided by the
AspectJ class library, but note that we're not using full-blown AspectJ
here—we're in fact using Spring AOP. Full AspectJ involves a special
aspect language and compiler to generate classes with the advice woven into
the class bytecode itself. Spring AOP on the other hand uses dynamic proxies
(either the interface variety that comes with Java, or else class proxies via
CGLIB) to advise classes. While Spring AOP borrows classes and also the
AspectJ pointcut language from AspectJ, its use of dynamic (runtime) proxies
as opposed to bytecode-level advice integration distinguishes it from AspectJ.
Now it's time to update our application context with our AOP configuration. We do this in listing 4 below.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-2.5.xsd>
<jee:jndi-lookup id="mailSession" jndi-name="mail/Session" resource-ref="true"/>
<bean id="mailSender"
class="org.springframework.mail.javamail.JavaMailSenderImpl"
p:session-ref="mailSession"/>
<bean id="mailingListService"
class="app.service.MailingListServiceImpl"
p:mailSender-ref="mailSender"/>
<bean id="forkAdvice" class="app.aop.ForkAdvice"/>
<aop:config>
<aop:aspect ref="forkAdvice">
<aop:around method="fork"
pointcut="execution(* org.springframework.mail.javamail.JavaMailSenderImpl.send(..))"/>
</aop:aspect>
</aop:config>
...
</beans>
This is similar to what we had before, but there are a couple of differences.
First, note that we've declared the aop namespace here. That of
course allows us to use the namespace configuration feature that Spring 2.0
introduced. The other change is that we've added a definition for our advice
bean as well as some AOP configuration. In aop:aspect we point to
our forkAdvice as the advising class to be applied, we indicate that
it will be "around" advice, we specify the advising method, and finally we specify
a pointcut that indicates which method calls will be advised/wrapped. We use the
AspectJ
pointcut language to specify a pointcut. Here we're indicating that we want
all calls to any of the JavaMailSenderImpl.send() methods to be
advised.
As mentioned previously, this technique is like the wrapper technique in that
you can use it to add the forking behavior in a way that's transparent to client
code. Moreover you can use it not just for JavaMail but really for any method
where you want to create a new thread before executing the method. You just
add the appropriate aop:around definitions to the aop:aspect
definition and you're in business.
Have fun!