CGLIB 3 has been released in order to depend on ASM 4, which Spring now depends on internally (see previous commit). This commit eliminates spring-beans' optional dependency on cglib-nodep v2.2 and instead repackages net.sf.cglib => org.springframework.cglib much in the same way we have historically done with ASM. This change is beneficial to users in several ways: - Eliminates the need to manually add CGLIB to the application classpath; especially important for the growing number of @Configuration class users. Java-based configuration functionality, along with proxy-target-class and method injection features now work 'out of the box' in Spring 3.2. - Eliminates the possibility of conflicts with other libraries that may dependend on differing versions of CGLIB, e.g. Hibernate 3.3.1.ga and its dependency on CGLIB 2.1.3 would easily cause a conflict if the application were depending on CGLIB 3 for Spring-related purposes. - Picks up CGLIB 3's changes to support ASM 4, meaning that CGLIB is that much less likely to work well in a Java 7 environment due to ASM 4's support for transforming classes with invokedynamic bytecode instructions. On CGLIB and ASM: CGLIB's own dependency on ASM is also transformed along the way to depend on Spring's repackaged org.springframework.asm, primarily to eliminate unnecessary duplication of ASM classfiles in spring-core and in the process save around 100K in the final spring-core JAR file size. It is coincidental that spring-core and CGLIB currently depend on the exact same version of ASM (4.0), but it is also unlikely to change any time soon. If this change does occur and versions of ASM drift, then the size optimization mentioned above will have to be abandoned. This would have no compatibility impact, however, so this is a reasonable solution now and for the forseeable future. On a mysterious NoClassDefFoundError: During the upgrade to CGLIB 3.0, Spring test cases began failing due to NoClassDefFoundErrors being thrown from CGLIB's DebuggingClassWriter regarding its use of asm-util's TraceClassVisitor type. previous versions of cglib-nodep, particularly 2.2, did not cause this behavior, even though cglib-nodep has never actually repackaged and bundled asm-util classes. The reason for these NoClassDefFoundErrors occurring now is still not fully understood, but appears to be due to subtle JVM bytecode preverification rules. The hypothesis is that due to minor changes in DebuggingClassWriter such as additional casts, access to instance variables declared in the superclass, and indeed a change in the superclass hierarchy, preverification may be kicking in on the toByteArray method body, at which point the reference to the missing TraceClassVisitor type is noticed and the NCDFE is thrown. For this reason, a dummy implementation of TraceClassVisitor has been added to spring-core in the org.springframework.asm.util package. This class simply ensures that Spring's own tests never result in the NCDFE described above, and more importantly that Spring's users never encounter the same. Other changes include: - rename package-private Cglib2AopProxy => CglibAopProxy - eliminate all 'cglibAvailable' checks, warnings and errors - eliminate all 'CGLIB2' language in favor of 'CGLIB' - eliminate all mention in reference and java docs of needing to add cglib(-nodep) to one's application classpath Issue: SPR-9669
1973 lines
88 KiB
XML
1973 lines
88 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
|
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0"
|
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
xmlns:xi="http://www.w3.org/2001/XInclude"
|
|
xml:id="aop-api">
|
|
<title>Spring AOP APIs</title>
|
|
|
|
<section id="aop-api-introduction">
|
|
<title>Introduction</title>
|
|
|
|
<para>The previous chapter described the Spring 2.0 and later version's
|
|
support for AOP using @AspectJ and schema-based aspect definitions. In
|
|
this chapter we discuss the lower-level Spring AOP APIs and the AOP
|
|
support used in Spring 1.2 applications. For new applications, we
|
|
recommend the use of the Spring 2.0 and later AOP support described in the
|
|
previous chapter, but when working with existing applications, or when
|
|
reading books and articles, you may come across Spring 1.2 style examples.
|
|
Spring 3.0 is backwards compatible with Spring 1.2 and everything
|
|
described in this chapter is fully supported in Spring 3.0.</para>
|
|
</section>
|
|
|
|
<section id="aop-api-pointcuts">
|
|
<title>Pointcut API in Spring</title>
|
|
|
|
<para>Let's look at how Spring handles the crucial pointcut
|
|
concept.</para>
|
|
|
|
<section id="aop-api-concepts">
|
|
<title>Concepts</title>
|
|
|
|
<para>Spring's pointcut model enables pointcut reuse independent of
|
|
advice types. It's possible to target different advice using the same
|
|
pointcut.</para>
|
|
|
|
<para>The <literal>org.springframework.aop.Pointcut</literal> interface
|
|
is the central interface, used to target advices to particular classes
|
|
and methods. The complete interface is shown below:</para>
|
|
|
|
<programlisting language="java">public interface Pointcut {
|
|
|
|
ClassFilter getClassFilter();
|
|
|
|
MethodMatcher getMethodMatcher();
|
|
|
|
}</programlisting>
|
|
|
|
<para>Splitting the <interfacename>Pointcut</interfacename> interface
|
|
into two parts allows reuse of class and method matching parts, and
|
|
fine-grained composition operations (such as performing a "union" with
|
|
another method matcher).</para>
|
|
|
|
<para>The <interfacename>ClassFilter</interfacename> interface is used
|
|
to restrict the pointcut to a given set of target classes. If the
|
|
<literal>matches()</literal> method always returns true, all target
|
|
classes will be matched:</para>
|
|
|
|
<programlisting language="java">public interface ClassFilter {
|
|
|
|
boolean matches(Class clazz);
|
|
}</programlisting>
|
|
|
|
<para>The <interfacename>MethodMatcher</interfacename> interface is
|
|
normally more important. The complete interface is shown below:</para>
|
|
|
|
<programlisting language="java">public interface MethodMatcher {
|
|
|
|
boolean matches(Method m, Class targetClass);
|
|
|
|
boolean isRuntime();
|
|
|
|
boolean matches(Method m, Class targetClass, Object[] args);
|
|
}</programlisting>
|
|
|
|
<para>The <literal>matches(Method, Class) </literal>method is used to
|
|
test whether this pointcut will ever match a given method on a target
|
|
class. This evaluation can be performed when an AOP proxy is created, to
|
|
avoid the need for a test on every method invocation. If the 2-argument
|
|
matches method returns true for a given method, and the
|
|
<literal>isRuntime()</literal> method for the MethodMatcher returns
|
|
true, the 3-argument matches method will be invoked on every method
|
|
invocation. This enables a pointcut to look at the arguments passed to
|
|
the method invocation immediately before the target advice is to
|
|
execute.</para>
|
|
|
|
<para>Most MethodMatchers are static, meaning that their
|
|
<literal>isRuntime()</literal> method returns false. In this case, the
|
|
3-argument matches method will never be invoked.</para>
|
|
|
|
<tip>
|
|
<para>If possible, try to make pointcuts static, allowing the AOP
|
|
framework to cache the results of pointcut evaluation when an AOP
|
|
proxy is created.</para>
|
|
</tip>
|
|
</section>
|
|
|
|
<section id="aop-api-pointcut-ops">
|
|
<title>Operations on pointcuts</title>
|
|
|
|
<para>Spring supports operations on pointcuts: notably,
|
|
<emphasis>union</emphasis> and <emphasis>intersection</emphasis>.</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>Union means the methods that either pointcut matches.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Intersection means the methods that both pointcuts
|
|
match.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Union is usually more useful.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Pointcuts can be composed using the static methods in the
|
|
<emphasis>org.springframework.aop.support.Pointcuts</emphasis>
|
|
class, or using the <emphasis>ComposablePointcut</emphasis> class in
|
|
the same package. However, using AspectJ pointcut expressions is
|
|
usually a simpler approach.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</section>
|
|
|
|
<section id="aop-api-pointcuts-aspectj">
|
|
<title>AspectJ expression pointcuts</title>
|
|
|
|
<para>Since 2.0, the most important type of pointcut used by Spring is
|
|
<literal>org.springframework.aop.aspectj.AspectJExpressionPointcut</literal>.
|
|
This is a pointcut that uses an AspectJ supplied library to parse an
|
|
AspectJ pointcut expression string.</para>
|
|
|
|
<para>See the previous chapter for a discussion of supported AspectJ
|
|
pointcut primitives.</para>
|
|
</section>
|
|
|
|
<section id="aop-api-pointcuts-impls">
|
|
<title>Convenience pointcut implementations</title>
|
|
|
|
<para>Spring provides several convenient pointcut implementations. Some
|
|
can be used out of the box; others are intended to be subclassed in
|
|
application-specific pointcuts.</para>
|
|
|
|
<section id="aop-api-pointcuts-static">
|
|
<title>Static pointcuts</title>
|
|
|
|
<para>Static pointcuts are based on method and target class, and
|
|
cannot take into account the method's arguments. Static pointcuts are
|
|
sufficient - <emphasis>and best</emphasis> - for most usages. It's
|
|
possible for Spring to evaluate a static pointcut only once, when a
|
|
method is first invoked: after that, there is no need to evaluate the
|
|
pointcut again with each method invocation.</para>
|
|
|
|
<para>Let's consider some static pointcut implementations included
|
|
with Spring.</para>
|
|
|
|
<section id="aop-api-pointcuts-regex">
|
|
<title>Regular expression pointcuts</title>
|
|
|
|
<para>One obvious way to specify static pointcuts is regular
|
|
expressions. Several AOP frameworks besides Spring make this
|
|
possible.
|
|
<literal>org.springframework.aop.support.JdkRegexpMethodPointcut</literal>
|
|
is a generic regular expression pointcut, using the regular
|
|
expression support in JDK 1.4+.</para>
|
|
|
|
<para>Using the <literal>JdkRegexpMethodPointcut</literal> class,
|
|
you can provide a list of pattern Strings. If any of these is a
|
|
match, the pointcut will evaluate to true. (So the result is
|
|
effectively the union of these pointcuts.)</para>
|
|
|
|
<para>The usage is shown below:</para>
|
|
|
|
<para><programlisting language="xml"><bean id="settersAndAbsquatulatePointcut"
|
|
class="org.springframework.aop.support.JdkRegexpMethodPointcut">
|
|
<property name="patterns">
|
|
<list>
|
|
<value>.*set.*</value>
|
|
<value>.*absquatulate</value>
|
|
</list>
|
|
</property>
|
|
</bean></programlisting></para>
|
|
|
|
<para>Spring provides a convenience class,
|
|
<literal>RegexpMethodPointcutAdvisor</literal>, that allows us to
|
|
also reference an Advice (remember that an Advice can be an
|
|
interceptor, before advice, throws advice etc.). Behind the scenes,
|
|
Spring will use a <literal>JdkRegexpMethodPointcut</literal>. Using
|
|
<literal>RegexpMethodPointcutAdvisor</literal> simplifies wiring, as
|
|
the one bean encapsulates both pointcut and advice, as shown
|
|
below:</para>
|
|
|
|
<para><programlisting language="xml"><bean id="settersAndAbsquatulateAdvisor"
|
|
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
|
|
<property name="advice">
|
|
<ref local="beanNameOfAopAllianceInterceptor"/>
|
|
</property>
|
|
<property name="patterns">
|
|
<list>
|
|
<value>.*set.*</value>
|
|
<value>.*absquatulate</value>
|
|
</list>
|
|
</property>
|
|
</bean></programlisting></para>
|
|
|
|
<para><emphasis>RegexpMethodPointcutAdvisor</emphasis> can be used
|
|
with any Advice type.</para>
|
|
</section>
|
|
|
|
<section id="aop-api-pointcuts-attribute-driven">
|
|
<title>Attribute-driven pointcuts</title>
|
|
|
|
<para>An important type of static pointcut is a
|
|
<emphasis>metadata-driven</emphasis> pointcut. This uses the values
|
|
of metadata attributes: typically, source-level metadata.</para>
|
|
</section>
|
|
</section>
|
|
|
|
<section id="aop-api-pointcuts-dynamic">
|
|
<title>Dynamic pointcuts</title>
|
|
|
|
<para>Dynamic pointcuts are costlier to evaluate than static
|
|
pointcuts. They take into account method
|
|
<emphasis>arguments</emphasis>, as well as static information. This
|
|
means that they must be evaluated with every method invocation; the
|
|
result cannot be cached, as arguments will vary.</para>
|
|
|
|
<para>The main example is the <literal>control flow</literal>
|
|
pointcut.</para>
|
|
|
|
<section id="aop-api-pointcuts-cflow">
|
|
<title>Control flow pointcuts</title>
|
|
|
|
<para>Spring control flow pointcuts are conceptually similar to
|
|
AspectJ <emphasis>cflow</emphasis> pointcuts, although less
|
|
powerful. (There is currently no way to specify that a pointcut
|
|
executes below a join point matched by another pointcut.) A control
|
|
flow pointcut matches the current call stack. For example, it might
|
|
fire if the join point was invoked by a method in the
|
|
<literal>com.mycompany.web</literal> package, or by the
|
|
<literal>SomeCaller</literal> class. Control flow pointcuts are
|
|
specified using the
|
|
<literal>org.springframework.aop.support.ControlFlowPointcut
|
|
</literal>class.<note>
|
|
<para>Control flow pointcuts are significantly more expensive to
|
|
evaluate at runtime than even other dynamic pointcuts. In Java
|
|
1.4, the cost is about 5 times that of other dynamic
|
|
pointcuts.</para>
|
|
</note></para>
|
|
</section>
|
|
</section>
|
|
</section>
|
|
|
|
<section id="aop-api-pointcuts-superclasses">
|
|
<title>Pointcut superclasses</title>
|
|
|
|
<para>Spring provides useful pointcut superclasses to help you to
|
|
implement your own pointcuts.</para>
|
|
|
|
<para>Because static pointcuts are most useful, you'll probably subclass
|
|
StaticMethodMatcherPointcut, as shown below. This requires implementing
|
|
just one abstract method (although it's possible to override other
|
|
methods to customize behavior):</para>
|
|
|
|
<para><programlisting language="java">class TestStaticPointcut extends StaticMethodMatcherPointcut {
|
|
|
|
public boolean matches(Method m, Class targetClass) {
|
|
// return true if custom criteria match
|
|
}
|
|
}</programlisting>There are also superclasses for dynamic pointcuts.</para>
|
|
|
|
<para>You can use custom pointcuts with any advice type in Spring 1.0
|
|
RC2 and above.</para>
|
|
</section>
|
|
|
|
<section id="aop-api-pointcuts-custom">
|
|
<title>Custom pointcuts</title>
|
|
|
|
<para>Because pointcuts in Spring AOP are Java classes, rather than
|
|
language features (as in AspectJ) it's possible to declare custom
|
|
pointcuts, whether static or dynamic. Custom pointcuts in Spring can be
|
|
arbitrarily complex. However, using the AspectJ pointcut expression
|
|
language is recommended if possible.</para>
|
|
|
|
<note>
|
|
<para>Later versions of Spring may offer support for "semantic
|
|
pointcuts" as offered by JAC: for example, "all methods that change
|
|
instance variables in the target object."</para>
|
|
</note>
|
|
</section>
|
|
</section>
|
|
|
|
<section id="aop-api-advice">
|
|
<title>Advice API in Spring</title>
|
|
|
|
<para>Let's now look at how Spring AOP handles advice.</para>
|
|
|
|
<section id="aop-api-advice-lifecycle">
|
|
<title>Advice lifecycles</title>
|
|
|
|
<para>Each advice is a Spring bean. An advice instance can be shared
|
|
across all advised objects, or unique to each advised object. This
|
|
corresponds to <emphasis>per-class</emphasis> or
|
|
<emphasis>per-instance</emphasis> advice.</para>
|
|
|
|
<para>Per-class advice is used most often. It is appropriate for generic
|
|
advice such as transaction advisors. These do not depend on the state of
|
|
the proxied object or add new state; they merely act on the method and
|
|
arguments.</para>
|
|
|
|
<para>Per-instance advice is appropriate for introductions, to support
|
|
mixins. In this case, the advice adds state to the proxied
|
|
object.</para>
|
|
|
|
<para>It's possible to use a mix of shared and per-instance advice in
|
|
the same AOP proxy.</para>
|
|
</section>
|
|
|
|
<section id="aop-api-advice-types">
|
|
<title>Advice types in Spring</title>
|
|
|
|
<para>Spring provides several advice types out of the box, and is
|
|
extensible to support arbitrary advice types. Let us look at the basic
|
|
concepts and standard advice types.</para>
|
|
|
|
<section id="aop-api-advice-around">
|
|
<title>Interception around advice</title>
|
|
|
|
<para>The most fundamental advice type in Spring is
|
|
<emphasis>interception around advice</emphasis>.</para>
|
|
|
|
<para>Spring is compliant with the AOP Alliance interface for around
|
|
advice using method interception. MethodInterceptors implementing
|
|
around advice should implement the following interface:</para>
|
|
|
|
<programlisting language="java">public interface MethodInterceptor extends Interceptor {
|
|
|
|
Object invoke(MethodInvocation invocation) throws Throwable;
|
|
}</programlisting>
|
|
|
|
<para>The <classname>MethodInvocation</classname> argument to the
|
|
<methodname>invoke()</methodname> method exposes the method being
|
|
invoked; the target join point; the AOP proxy; and the arguments to
|
|
the method. The <methodname>invoke()</methodname> method should return
|
|
the invocation's result: the return value of the join point.</para>
|
|
|
|
<para>A simple <classname>MethodInterceptor</classname> implementation
|
|
looks as follows:</para>
|
|
|
|
<programlisting language="java">public class DebugInterceptor implements MethodInterceptor {
|
|
|
|
public Object invoke(MethodInvocation invocation) throws Throwable {
|
|
System.out.println("Before: invocation=[" + invocation + "]");
|
|
Object rval = invocation.proceed();
|
|
System.out.println("Invocation returned");
|
|
return rval;
|
|
}
|
|
}</programlisting>
|
|
|
|
<para>Note the call to the MethodInvocation's
|
|
<methodname>proceed()</methodname> method. This proceeds down the
|
|
interceptor chain towards the join point. Most interceptors will
|
|
invoke this method, and return its return value. However, a
|
|
MethodInterceptor, like any around advice, can return a different
|
|
value or throw an exception rather than invoke the proceed method.
|
|
However, you don't want to do this without good reason!</para>
|
|
|
|
<note>
|
|
<para>MethodInterceptors offer interoperability with other AOP
|
|
Alliance-compliant AOP implementations. The other advice types
|
|
discussed in the remainder of this section implement common AOP
|
|
concepts, but in a Spring-specific way. While there is an advantage
|
|
in using the most specific advice type, stick with MethodInterceptor
|
|
around advice if you are likely to want to run the aspect in another
|
|
AOP framework. Note that pointcuts are not currently interoperable
|
|
between frameworks, and the AOP Alliance does not currently define
|
|
pointcut interfaces.</para>
|
|
</note>
|
|
</section>
|
|
|
|
<section id="aop-api-advice-before">
|
|
<title>Before advice</title>
|
|
|
|
<para>A simpler advice type is a <emphasis role="bold">before
|
|
advice</emphasis>. This does not need a
|
|
<literal>MethodInvocation</literal> object, since it will only be
|
|
called before entering the method.</para>
|
|
|
|
<para>The main advantage of a before advice is that there is no need
|
|
to invoke the <literal>proceed() </literal>method, and therefore no
|
|
possibility of inadvertently failing to proceed down the interceptor
|
|
chain.</para>
|
|
|
|
<para>The <literal>MethodBeforeAdvice</literal> interface is shown
|
|
below. (Spring's API design would allow for field before advice,
|
|
although the usual objects apply to field interception and it's
|
|
unlikely that Spring will ever implement it).</para>
|
|
|
|
<programlisting language="java">public interface MethodBeforeAdvice extends BeforeAdvice {
|
|
|
|
void before(Method m, Object[] args, Object target) throws Throwable;
|
|
}</programlisting>
|
|
|
|
<para>Note the return type is <literal>void</literal>. Before advice
|
|
can insert custom behavior before the join point executes, but cannot
|
|
change the return value. If a before advice throws an exception, this
|
|
will abort further execution of the interceptor chain. The exception
|
|
will propagate back up the interceptor chain. If it is unchecked, or
|
|
on the signature of the invoked method, it will be passed directly to
|
|
the client; otherwise it will be wrapped in an unchecked exception by
|
|
the AOP proxy.</para>
|
|
|
|
<para>An example of a before advice in Spring, which counts all method
|
|
invocations:</para>
|
|
|
|
<programlisting language="java">public class CountingBeforeAdvice implements MethodBeforeAdvice {
|
|
|
|
private int count;
|
|
|
|
public void before(Method m, Object[] args, Object target) throws Throwable {
|
|
++count;
|
|
}
|
|
|
|
public int getCount() {
|
|
return count;
|
|
}
|
|
}</programlisting>
|
|
|
|
<tip>
|
|
<para>Before advice can be used with any pointcut.</para>
|
|
</tip>
|
|
</section>
|
|
|
|
<section id="aop-api-advice-throws">
|
|
<title>Throws advice</title>
|
|
|
|
<para><emphasis role="bold">Throws advice</emphasis> is invoked after
|
|
the return of the join point if the join point threw an exception.
|
|
Spring offers typed throws advice. Note that this means that the
|
|
<literal>org.springframework.aop.ThrowsAdvice</literal> interface does
|
|
not contain any methods: It is a tag interface identifying that the
|
|
given object implements one or more typed throws advice methods. These
|
|
should be in the form of:</para>
|
|
|
|
<programlisting language="java">afterThrowing([Method, args, target], subclassOfThrowable) </programlisting>
|
|
|
|
<para>Only the last argument is required. The method signatures may
|
|
have either one or four arguments, depending on whether the advice
|
|
method is interested in the method and arguments. The following
|
|
classes are examples of throws advice.</para>
|
|
|
|
<para>The advice below is invoked if a
|
|
<exceptionname>RemoteException</exceptionname> is thrown (including
|
|
subclasses):</para>
|
|
|
|
<programlisting language="java">public class RemoteThrowsAdvice implements ThrowsAdvice {
|
|
|
|
public void afterThrowing(RemoteException ex) throws Throwable {
|
|
<lineannotation>// Do something with remote exception</lineannotation>
|
|
}
|
|
}</programlisting>
|
|
|
|
<para>The following advice is invoked if a
|
|
<exceptionname>ServletException</exceptionname> is thrown. Unlike the
|
|
above advice, it declares 4 arguments, so that it has access to the
|
|
invoked method, method arguments and target object:</para>
|
|
|
|
<programlisting language="java">public class ServletThrowsAdviceWithArguments implements ThrowsAdvice {
|
|
|
|
public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) {
|
|
<lineannotation>// Do something with all arguments</lineannotation>
|
|
}
|
|
}</programlisting>
|
|
|
|
<para>The final example illustrates how these two methods could be
|
|
used in a single class, which handles both
|
|
<literal>RemoteException</literal> and
|
|
<literal>ServletException</literal>. Any number of throws advice
|
|
methods can be combined in a single class.</para>
|
|
|
|
<programlisting language="java">public static class CombinedThrowsAdvice implements ThrowsAdvice {
|
|
|
|
public void afterThrowing(RemoteException ex) throws Throwable {
|
|
// Do something with remote exception
|
|
}
|
|
|
|
public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) {
|
|
// Do something with all arguments
|
|
}
|
|
}</programlisting>
|
|
|
|
<para><emphasis>Note:</emphasis> If a throws-advice method throws an
|
|
exception itself, it will override the original exception (i.e. change
|
|
the exception thrown to the user). The overriding exception will
|
|
typically be a RuntimeException; this is compatible with any method
|
|
signature. However, if a throws-advice method throws a checked
|
|
exception, it will have to match the declared exceptions of the target
|
|
method and is hence to some degree coupled to specific target method
|
|
signatures. <emphasis>Do not throw an undeclared checked exception
|
|
that is incompatible with the target method's
|
|
signature!</emphasis></para>
|
|
|
|
<tip>
|
|
<para>Throws advice can be used with any pointcut.</para>
|
|
</tip>
|
|
</section>
|
|
|
|
<section id="aop-api-advice-after-returning">
|
|
<title>After Returning advice</title>
|
|
|
|
<para>An after returning advice in Spring must implement the
|
|
<emphasis>org.springframework.aop.AfterReturningAdvice</emphasis>
|
|
interface, shown below:</para>
|
|
|
|
<programlisting language="java">public interface AfterReturningAdvice extends Advice {
|
|
|
|
void afterReturning(Object returnValue, Method m, Object[] args, Object target)
|
|
throws Throwable;
|
|
}</programlisting>
|
|
|
|
<para>An after returning advice has access to the return value (which
|
|
it cannot modify), invoked method, methods arguments and
|
|
target.</para>
|
|
|
|
<para>The following after returning advice counts all successful
|
|
method invocations that have not thrown exceptions:</para>
|
|
|
|
<programlisting language="java">public class CountingAfterReturningAdvice implements AfterReturningAdvice {
|
|
|
|
private int count;
|
|
|
|
public void afterReturning(Object returnValue, Method m, Object[] args, Object target)
|
|
throws Throwable {
|
|
++count;
|
|
}
|
|
|
|
public int getCount() {
|
|
return count;
|
|
}
|
|
}</programlisting>
|
|
|
|
<para>This advice doesn't change the execution path. If it throws an
|
|
exception, this will be thrown up the interceptor chain instead of the
|
|
return value.</para>
|
|
|
|
<tip>
|
|
<para>After returning advice can be used with any pointcut.</para>
|
|
</tip>
|
|
</section>
|
|
|
|
<section id="aop-api-advice-introduction">
|
|
|
|
|
|
<title>Introduction advice</title>
|
|
|
|
|
|
|
|
<para>Spring treats introduction advice as a special kind of
|
|
interception advice.</para>
|
|
|
|
|
|
|
|
<para>Introduction requires an <literal>IntroductionAdvisor</literal>,
|
|
and an <literal>IntroductionInterceptor</literal>, implementing the
|
|
following interface:</para>
|
|
|
|
|
|
|
|
<programlisting language="java">public interface IntroductionInterceptor extends MethodInterceptor {
|
|
|
|
boolean implementsInterface(Class intf);
|
|
}</programlisting>
|
|
|
|
|
|
|
|
<para>The <literal>invoke() </literal>method inherited from the AOP
|
|
Alliance <literal>MethodInterceptor</literal> interface must implement
|
|
the introduction: that is, if the invoked method is on an introduced
|
|
interface, the introduction interceptor is responsible for handling
|
|
the method call - it cannot invoke
|
|
<literal>proceed()</literal>.</para>
|
|
|
|
|
|
|
|
<para>Introduction advice cannot be used with any pointcut, as it
|
|
applies only at class, rather than method, level. You can only use
|
|
introduction advice with the <literal>IntroductionAdvisor</literal>,
|
|
which has the following methods:</para>
|
|
|
|
|
|
|
|
<programlisting language="java">public interface IntroductionAdvisor extends Advisor, IntroductionInfo {
|
|
|
|
ClassFilter getClassFilter();
|
|
|
|
void validateInterfaces() throws IllegalArgumentException;
|
|
}
|
|
|
|
public interface IntroductionInfo {
|
|
|
|
Class[] getInterfaces();
|
|
}</programlisting>
|
|
|
|
|
|
|
|
<para>There is no <interfacename>MethodMatcher</interfacename>, and
|
|
hence no <interfacename>Pointcut</interfacename>, associated with
|
|
introduction advice. Only class filtering is logical.</para>
|
|
|
|
|
|
|
|
<para>The <literal>getInterfaces()</literal> method returns the
|
|
interfaces introduced by this advisor.</para>
|
|
|
|
The
|
|
|
|
<literal>validateInterfaces()</literal>
|
|
|
|
method is used internally to see whether or not the introduced interfaces can be implemented by the configured
|
|
|
|
<literal>IntroductionInterceptor</literal>
|
|
|
|
.
|
|
|
|
<para>Let's look at a simple example from the Spring test suite. Let's
|
|
suppose we want to introduce the following interface to one or more
|
|
objects:</para>
|
|
|
|
|
|
|
|
<para>
|
|
<programlisting language="java">public interface Lockable {
|
|
void lock();
|
|
void unlock();
|
|
boolean locked();
|
|
}</programlisting>
|
|
</para>
|
|
|
|
|
|
|
|
<para>This illustrates a <emphasis role="bold">mixin</emphasis>. We
|
|
want to be able to cast advised objects to Lockable, whatever their
|
|
type, and call lock and unlock methods. If we call the lock() method,
|
|
we want all setter methods to throw a
|
|
<literal>LockedException</literal>. Thus we can add an aspect that
|
|
provides the ability to make objects immutable, without them having
|
|
any knowledge of it: a good example of AOP.</para>
|
|
|
|
|
|
|
|
<para>Firstly, we'll need an
|
|
<literal>IntroductionInterceptor</literal> that does the heavy
|
|
lifting. In this case, we extend the
|
|
<literal>org.springframework.aop.support.DelegatingIntroductionInterceptor</literal>
|
|
convenience class. We could implement IntroductionInterceptor
|
|
directly, but using
|
|
<literal>DelegatingIntroductionInterceptor</literal> is best for most
|
|
cases.</para>
|
|
|
|
|
|
|
|
<para>The <literal>DelegatingIntroductionInterceptor</literal> is
|
|
designed to delegate an introduction to an actual implementation of
|
|
the introduced interface(s), concealing the use of interception to do
|
|
so. The delegate can be set to any object using a constructor
|
|
argument; the default delegate (when the no-arg constructor is used)
|
|
is this. Thus in the example below, the delegate is the
|
|
<literal>LockMixin</literal> subclass of
|
|
<literal>DelegatingIntroductionInterceptor</literal>. Given a delegate
|
|
(by default itself), a
|
|
<literal>DelegatingIntroductionInterceptor</literal> instance looks
|
|
for all interfaces implemented by the delegate (other than
|
|
IntroductionInterceptor), and will support introductions against any
|
|
of them. It's possible for subclasses such as
|
|
<literal>LockMixin</literal> to call the
|
|
<literal>suppressInterface(Class intf) </literal>method to suppress
|
|
interfaces that should not be exposed. However, no matter how many
|
|
interfaces an <literal>IntroductionInterceptor</literal> is prepared
|
|
to support, the <literal>IntroductionAdvisor</literal> used will
|
|
control which interfaces are actually exposed. An introduced interface
|
|
will conceal any implementation of the same interface by the
|
|
target.</para>
|
|
|
|
|
|
|
|
<para>Thus LockMixin subclasses
|
|
<literal>DelegatingIntroductionInterceptor</literal> and implements
|
|
Lockable itself. The superclass automatically picks up that Lockable
|
|
can be supported for introduction, so we don't need to specify that.
|
|
We could introduce any number of interfaces in this way.</para>
|
|
|
|
|
|
|
|
<para>Note the use of the <literal>locked</literal> instance variable.
|
|
This effectively adds additional state to that held in the target
|
|
object.</para>
|
|
|
|
|
|
|
|
<para>
|
|
<programlisting language="java">public class LockMixin extends DelegatingIntroductionInterceptor
|
|
implements Lockable {
|
|
|
|
private boolean locked;
|
|
|
|
public void lock() {
|
|
this.locked = true;
|
|
}
|
|
|
|
public void unlock() {
|
|
this.locked = false;
|
|
}
|
|
|
|
public boolean locked() {
|
|
return this.locked;
|
|
}
|
|
|
|
public Object invoke(MethodInvocation invocation) throws Throwable {
|
|
if (locked() && invocation.getMethod().getName().indexOf("set") == 0)
|
|
throw new LockedException();
|
|
return super.invoke(invocation);
|
|
}
|
|
|
|
}</programlisting>
|
|
</para>
|
|
|
|
|
|
|
|
<para>Often it isn't necessary to override the <literal>invoke()
|
|
</literal>method: the
|
|
<literal>DelegatingIntroductionInterceptor</literal> implementation -
|
|
which calls the delegate method if the method is introduced, otherwise
|
|
proceeds towards the join point - is usually sufficient. In the
|
|
present case, we need to add a check: no setter method can be invoked
|
|
if in locked mode.</para>
|
|
|
|
|
|
|
|
<para>The introduction advisor required is simple. All it needs to do
|
|
is hold a distinct <literal>LockMixin</literal> instance, and specify
|
|
the introduced interfaces - in this case, just
|
|
<literal>Lockable</literal>. A more complex example might take a
|
|
reference to the introduction interceptor (which would be defined as a
|
|
prototype): in this case, there's no configuration relevant for a
|
|
<literal>LockMixin</literal>, so we simply create it using
|
|
<literal>new</literal>.</para>
|
|
|
|
|
|
|
|
<para>
|
|
<programlisting language="java">public class LockMixinAdvisor extends DefaultIntroductionAdvisor {
|
|
|
|
public LockMixinAdvisor() {
|
|
super(new LockMixin(), Lockable.class);
|
|
}
|
|
}</programlisting>
|
|
</para>
|
|
|
|
|
|
|
|
<para>We can apply this advisor very simply: it requires no
|
|
configuration. (However, it <emphasis>is</emphasis> necessary: It's
|
|
impossible to use an <literal>IntroductionInterceptor</literal>
|
|
without an <emphasis>IntroductionAdvisor</emphasis>.) As usual with
|
|
introductions, the advisor must be per-instance, as it is stateful. We
|
|
need a different instance of <literal>LockMixinAdvisor</literal>, and
|
|
hence <literal>LockMixin</literal>, for each advised object. The
|
|
advisor comprises part of the advised object's state.</para>
|
|
|
|
|
|
|
|
<para>We can apply this advisor programmatically, using the
|
|
<literal>Advised.addAdvisor() </literal>method, or (the recommended
|
|
way) in XML configuration, like any other advisor. All proxy creation
|
|
choices discussed below, including "auto proxy creators," correctly
|
|
handle introductions and stateful mixins.</para>
|
|
|
|
|
|
</section>
|
|
</section>
|
|
</section>
|
|
|
|
<section id="aop-api-advisor">
|
|
<title>Advisor API in Spring</title>
|
|
|
|
<para>In Spring, an Advisor is an aspect that contains just a single
|
|
advice object associated with a pointcut expression.</para>
|
|
|
|
<para>Apart from the special case of introductions, any advisor can be
|
|
used with any advice.
|
|
<literal>org.springframework.aop.support.DefaultPointcutAdvisor</literal>
|
|
is the most commonly used advisor class. For example, it can be used with
|
|
a <literal>MethodInterceptor</literal>, <literal>BeforeAdvice</literal> or
|
|
<literal>ThrowsAdvice</literal>.</para>
|
|
|
|
<para>It is possible to mix advisor and advice types in Spring in the same
|
|
AOP proxy. For example, you could use a interception around advice, throws
|
|
advice and before advice in one proxy configuration: Spring will
|
|
automatically create the necessary interceptor chain.</para>
|
|
</section>
|
|
|
|
<section id="aop-pfb">
|
|
<title>Using the ProxyFactoryBean to create AOP proxies</title>
|
|
|
|
<para>If you're using the Spring IoC container (an ApplicationContext or
|
|
BeanFactory) for your business objects - and you should be! - you will
|
|
want to use one of Spring's AOP FactoryBeans. (Remember that a factory
|
|
bean introduces a layer of indirection, enabling it to create objects of a
|
|
different type.)</para>
|
|
|
|
<note>
|
|
<para>The Spring 2.0 AOP support also uses factory beans under the
|
|
covers.</para>
|
|
</note>
|
|
|
|
<para>The basic way to create an AOP proxy in Spring is to use the
|
|
<emphasis>org.springframework.aop.framework.ProxyFactoryBean</emphasis>.
|
|
This gives complete control over the pointcuts and advice that will apply,
|
|
and their ordering. However, there are simpler options that are preferable
|
|
if you don't need such control.</para>
|
|
|
|
<section id="aop-pfb-1">
|
|
<title>Basics</title>
|
|
|
|
<para>The <literal>ProxyFactoryBean</literal>, like other Spring
|
|
<literal>FactoryBean</literal> implementations, introduces a level of
|
|
indirection. If you define a <literal>ProxyFactoryBean</literal> with
|
|
name <literal>foo</literal>, what objects referencing
|
|
<literal>foo</literal> see is not the
|
|
<literal>ProxyFactoryBean</literal> instance itself, but an object
|
|
created by the <literal>ProxyFactoryBean</literal>'s implementation of
|
|
the <literal>getObject() </literal>method. This method will create an
|
|
AOP proxy wrapping a target object.</para>
|
|
|
|
<para>One of the most important benefits of using a
|
|
<literal>ProxyFactoryBean</literal> or another IoC-aware class to create
|
|
AOP proxies, is that it means that advices and pointcuts can also be
|
|
managed by IoC. This is a powerful feature, enabling certain approaches
|
|
that are hard to achieve with other AOP frameworks. For example, an
|
|
advice may itself reference application objects (besides the target,
|
|
which should be available in any AOP framework), benefiting from all the
|
|
pluggability provided by Dependency Injection.</para>
|
|
</section>
|
|
|
|
<section id="aop-pfb-2">
|
|
<title>JavaBean properties</title>
|
|
|
|
<para>In common with most <interfacename>FactoryBean</interfacename>
|
|
implementations provided with Spring, the
|
|
<classname>ProxyFactoryBean</classname> class is itself a JavaBean. Its
|
|
properties are used to:</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>Specify the target you want to proxy.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Specify whether to use CGLIB (see below and also
|
|
<xref linkend="aop-pfb-proxy-types" />).</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>Some key properties are inherited from
|
|
<classname>org.springframework.aop.framework.ProxyConfig</classname>
|
|
(the superclass for all AOP proxy factories in Spring). These key
|
|
properties include:</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><literal>proxyTargetClass</literal>: <literal>true</literal>
|
|
if the target class is to be proxied, rather than the target class'
|
|
interfaces. If this property value is set to
|
|
<literal>true</literal>, then CGLIB proxies will be created (but see
|
|
also <xref linkend="aop-pfb-proxy-types" />).</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><literal>optimize</literal>: controls whether or not
|
|
aggressive optimizations are applied to proxies <emphasis>created
|
|
via CGLIB</emphasis>. One should not blithely use this setting
|
|
unless one fully understands how the relevant AOP proxy handles
|
|
optimization. This is currently used only for CGLIB proxies; it has
|
|
no effect with JDK dynamic proxies.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><literal>frozen</literal>: if a proxy configuration is
|
|
<literal>frozen</literal>, then changes to the configuration are no
|
|
longer allowed. This is useful both as a slight optimization and for
|
|
those cases when you don't want callers to be able to manipulate the
|
|
proxy (via the <interfacename>Advised</interfacename> interface)
|
|
after the proxy has been created. The default value of this property
|
|
is <literal>false</literal>, so changes such as adding additional
|
|
advice are allowed.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><literal>exposeProxy</literal>: determines whether or not the
|
|
current proxy should be exposed in a
|
|
<classname>ThreadLocal</classname> so that it can be accessed by the
|
|
target. If a target needs to obtain the proxy and the
|
|
<literal>exposeProxy</literal> property is set to
|
|
<literal>true</literal>, the target can use the
|
|
<methodname>AopContext.currentProxy()</methodname> method.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>Other properties specific to
|
|
<classname>ProxyFactoryBean</classname> include:</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><literal>proxyInterfaces</literal>: array of String interface
|
|
names. If this isn't supplied, a CGLIB proxy for the target class
|
|
will be used (but see also <xref linkend="aop-pfb-proxy-types" />).</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><literal>interceptorNames</literal>: String array of
|
|
<interfacename>Advisor</interfacename>, interceptor or other advice
|
|
names to apply. Ordering is significant, on a first come-first
|
|
served basis. That is to say that the first interceptor in the list
|
|
will be the first to be able to intercept the invocation.</para>
|
|
|
|
<para>The names are bean names in the current factory, including
|
|
bean names from ancestor factories. You can't mention bean
|
|
references here since doing so would result in the
|
|
<classname>ProxyFactoryBean</classname> ignoring the singleton
|
|
setting of the advice.</para>
|
|
|
|
<para>You can append an interceptor name with an asterisk
|
|
(<literal>*</literal>). This will result in the application of all
|
|
advisor beans with names starting with the part before the asterisk
|
|
to be applied. An example of using this feature can be found in
|
|
<xref linkend="aop-global-advisors" />.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>singleton: whether or not the factory should return a single
|
|
object, no matter how often the <literal>getObject()</literal>
|
|
method is called. Several <interfacename>FactoryBean</interfacename>
|
|
implementations offer such a method. The default value is
|
|
<literal>true</literal>. If you want to use stateful advice - for
|
|
example, for stateful mixins - use prototype advices along with a
|
|
singleton value of <literal>false</literal>.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</section>
|
|
|
|
<section id="aop-pfb-proxy-types">
|
|
<title>JDK- and CGLIB-based proxies</title>
|
|
|
|
<para>This section serves as the definitive documentation on how the
|
|
<classname>ProxyFactoryBean</classname> chooses to create one of either
|
|
a JDK- and CGLIB-based proxy for a particular target object (that is to
|
|
be proxied).</para>
|
|
|
|
<note>
|
|
<para>The behavior of the <classname>ProxyFactoryBean</classname> with
|
|
regard to creating JDK- or CGLIB-based proxies changed between
|
|
versions 1.2.x and 2.0 of Spring. The
|
|
<classname>ProxyFactoryBean</classname> now exhibits similar semantics
|
|
with regard to auto-detecting interfaces as those of the
|
|
<classname>TransactionProxyFactoryBean</classname> class.</para>
|
|
</note>
|
|
|
|
<para>If the class of a target object that is to be proxied (hereafter
|
|
simply referred to as the target class) doesn't implement any
|
|
interfaces, then a CGLIB-based proxy will be created. This is the
|
|
easiest scenario, because JDK proxies are interface based, and no
|
|
interfaces means JDK proxying isn't even possible. One simply plugs in
|
|
the target bean, and specifies the list of interceptors via the
|
|
<literal>interceptorNames</literal> property. Note that a CGLIB-based
|
|
proxy will be created even if the <literal>proxyTargetClass</literal>
|
|
property of the <classname>ProxyFactoryBean</classname> has been set to
|
|
<literal>false</literal>. (Obviously this makes no sense, and is best
|
|
removed from the bean definition because it is at best redundant, and at
|
|
worst confusing.)</para>
|
|
|
|
<para>If the target class implements one (or more) interfaces, then the
|
|
type of proxy that is created depends on the configuration of the
|
|
<classname>ProxyFactoryBean</classname>.</para>
|
|
|
|
<para>If the <literal>proxyTargetClass</literal> property of the
|
|
<classname>ProxyFactoryBean</classname> has been set to
|
|
<literal>true</literal>, then a CGLIB-based proxy will be created. This
|
|
makes sense, and is in keeping with the principle of least surprise.
|
|
Even if the <literal>proxyInterfaces</literal> property of the
|
|
<classname>ProxyFactoryBean</classname> has been set to one or more
|
|
fully qualified interface names, the fact that the
|
|
<literal>proxyTargetClass</literal> property is set to
|
|
<literal>true</literal> <emphasis>will</emphasis> cause CGLIB-based
|
|
proxying to be in effect.</para>
|
|
|
|
<para>If the <literal>proxyInterfaces</literal> property of the
|
|
<classname>ProxyFactoryBean</classname> has been set to one or more
|
|
fully qualified interface names, then a JDK-based proxy will be created.
|
|
The created proxy will implement all of the interfaces that were
|
|
specified in the <literal>proxyInterfaces</literal> property; if the
|
|
target class happens to implement a whole lot more interfaces than those
|
|
specified in the <literal>proxyInterfaces</literal> property, that is
|
|
all well and good but those additional interfaces will not be
|
|
implemented by the returned proxy.</para>
|
|
|
|
<para>If the <literal>proxyInterfaces</literal> property of the
|
|
<classname>ProxyFactoryBean</classname> has <emphasis>not</emphasis>
|
|
been set, but the target class <emphasis>does implement one (or
|
|
more)</emphasis> interfaces, then the
|
|
<classname>ProxyFactoryBean</classname> will auto-detect the fact that
|
|
the target class does actually implement at least one interface, and a
|
|
JDK-based proxy will be created. The interfaces that are actually
|
|
proxied will be <emphasis>all</emphasis> of the interfaces that the
|
|
target class implements; in effect, this is the same as simply supplying
|
|
a list of each and every interface that the target class implements to
|
|
the <literal>proxyInterfaces</literal> property. However, it is
|
|
significantly less work, and less prone to typos.</para>
|
|
</section>
|
|
|
|
<section id="aop-api-proxying-intf">
|
|
<title>Proxying interfaces</title>
|
|
|
|
<para>Let's look at a simple example of
|
|
<classname>ProxyFactoryBean</classname> in action. This example
|
|
involves:</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>A <emphasis>target bean</emphasis> that will be proxied. This
|
|
is the "personTarget" bean definition in the example below.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>An Advisor and an Interceptor used to provide advice.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>An AOP proxy bean definition specifying the target object (the
|
|
personTarget bean) and the interfaces to proxy, along with the
|
|
advices to apply.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para><programlisting language="xml"><bean id="personTarget" class="com.mycompany.PersonImpl">
|
|
<property name="name" value="Tony"/>
|
|
<property name="age" value="51"/>
|
|
</bean>
|
|
|
|
<bean id="myAdvisor" class="com.mycompany.MyAdvisor">
|
|
<property name="someProperty" value="Custom string property value"/>
|
|
</bean>
|
|
|
|
<bean id="debugInterceptor" class="org.springframework.aop.interceptor.DebugInterceptor">
|
|
</bean>
|
|
|
|
<bean id="person"
|
|
class="org.springframework.aop.framework.ProxyFactoryBean">
|
|
<property name="proxyInterfaces" value="com.mycompany.Person"/>
|
|
|
|
<property name="target" ref="personTarget"/>
|
|
<property name="interceptorNames">
|
|
<list>
|
|
<value>myAdvisor</value>
|
|
<value>debugInterceptor</value>
|
|
</list>
|
|
</property>
|
|
</bean></programlisting></para>
|
|
|
|
<para>Note that the <literal>interceptorNames</literal> property takes a
|
|
list of String: the bean names of the interceptor or advisors in the
|
|
current factory. Advisors, interceptors, before, after returning and
|
|
throws advice objects can be used. The ordering of advisors is
|
|
significant.</para>
|
|
|
|
<note>
|
|
<para>You might be wondering why the list doesn't hold bean
|
|
references. The reason for this is that if the ProxyFactoryBean's
|
|
singleton property is set to false, it must be able to return
|
|
independent proxy instances. If any of the advisors is itself a
|
|
prototype, an independent instance would need to be returned, so it's
|
|
necessary to be able to obtain an instance of the prototype from the
|
|
factory; holding a reference isn't sufficient.</para>
|
|
</note>
|
|
|
|
<para>The "person" bean definition above can be used in place of a
|
|
Person implementation, as follows:</para>
|
|
|
|
<programlisting language="java">Person person = (Person) factory.getBean("person");</programlisting>
|
|
|
|
<para>Other beans in the same IoC context can express a strongly typed
|
|
dependency on it, as with an ordinary Java object:</para>
|
|
|
|
<para><programlisting language="xml"><bean id="personUser" class="com.mycompany.PersonUser">
|
|
<property name="person"><ref local="person"/></property>
|
|
</bean></programlisting></para>
|
|
|
|
<para>The <literal>PersonUser</literal> class in this example would
|
|
expose a property of type Person. As far as it's concerned, the AOP
|
|
proxy can be used transparently in place of a "real" person
|
|
implementation. However, its class would be a dynamic proxy class. It
|
|
would be possible to cast it to the <literal>Advised</literal> interface
|
|
(discussed below).</para>
|
|
|
|
<para>It's possible to conceal the distinction between target and proxy
|
|
using an anonymous <emphasis>inner bean</emphasis>, as follows. Only the
|
|
<literal>ProxyFactoryBean</literal> definition is different; the advice
|
|
is included only for completeness:</para>
|
|
|
|
<para><programlisting language="xml"><bean id="myAdvisor" class="com.mycompany.MyAdvisor">
|
|
<property name="someProperty" value="Custom string property value"/>
|
|
</bean>
|
|
|
|
<bean id="debugInterceptor" class="org.springframework.aop.interceptor.DebugInterceptor"/>
|
|
|
|
<bean id="person" class="org.springframework.aop.framework.ProxyFactoryBean">
|
|
<property name="proxyInterfaces" value="com.mycompany.Person"/>
|
|
<!-- Use inner bean, not local reference to target -->
|
|
<property name="target">
|
|
<bean class="com.mycompany.PersonImpl">
|
|
<property name="name" value="Tony"/>
|
|
<property name="age" value="51"/>
|
|
</bean>
|
|
</property>
|
|
<property name="interceptorNames">
|
|
<list>
|
|
<value>myAdvisor</value>
|
|
<value>debugInterceptor</value>
|
|
</list>
|
|
</property>
|
|
</bean></programlisting></para>
|
|
|
|
<para>This has the advantage that there's only one object of type
|
|
<literal>Person</literal>: useful if we want to prevent users of the
|
|
application context from obtaining a reference to the un-advised object,
|
|
or need to avoid any ambiguity with Spring IoC
|
|
<emphasis>autowiring</emphasis>. There's also arguably an advantage in
|
|
that the ProxyFactoryBean definition is self-contained. However, there
|
|
are times when being able to obtain the un-advised target from the
|
|
factory might actually be an <emphasis>advantage</emphasis>: for
|
|
example, in certain test scenarios.</para>
|
|
</section>
|
|
|
|
<section id="aop-api-proxying-class">
|
|
<title>Proxying classes</title>
|
|
|
|
<para>What if you need to proxy a class, rather than one or more
|
|
interfaces?</para>
|
|
|
|
<para>Imagine that in our example above, there was no
|
|
<literal>Person</literal> interface: we needed to advise a class called
|
|
<literal>Person</literal> that didn't implement any business interface.
|
|
In this case, you can configure Spring to use CGLIB proxying, rather
|
|
than dynamic proxies. Simply set the <literal>proxyTargetClass</literal>
|
|
property on the ProxyFactoryBean above to true. While it's best to
|
|
program to interfaces, rather than classes, the ability to advise
|
|
classes that don't implement interfaces can be useful when working with
|
|
legacy code. (In general, Spring isn't prescriptive. While it makes it
|
|
easy to apply good practices, it avoids forcing a particular
|
|
approach.)</para>
|
|
|
|
<para>If you want to, you can force the use of CGLIB in any case, even
|
|
if you do have interfaces.</para>
|
|
|
|
<para>CGLIB proxying works by generating a subclass of the target class
|
|
at runtime. Spring configures this generated subclass to delegate method
|
|
calls to the original target: the subclass is used to implement the
|
|
<emphasis>Decorator</emphasis> pattern, weaving in the advice.</para>
|
|
|
|
<para>CGLIB proxying should generally be transparent to users. However,
|
|
there are some issues to consider:</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><literal>Final</literal> methods can't be advised, as they
|
|
can't be overridden.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>There is no need to add CGLIB to your classpath. As of Spring
|
|
3.2, CGLIB is repackaged and included in the spring-core JAR. In
|
|
other words, CGLIB-based AOP will work "out of the box" just as do
|
|
JDK dynamic proxies.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>There's little performance difference between CGLIB proxying and
|
|
dynamic proxies. As of Spring 1.0, dynamic proxies are slightly faster.
|
|
However, this may change in the future. Performance should not be a
|
|
decisive consideration in this case.</para>
|
|
</section>
|
|
|
|
<section id="aop-global-advisors">
|
|
<title>Using 'global' advisors</title>
|
|
|
|
<para>By appending an asterisk to an interceptor name, all advisors with
|
|
bean names matching the part before the asterisk, will be added to the
|
|
advisor chain. This can come in handy if you need to add a standard set
|
|
of 'global' advisors: <programlisting language="xml">
|
|
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
|
|
<property name="target" ref="service"/>
|
|
<property name="interceptorNames">
|
|
<list>
|
|
<value>global*</value>
|
|
</list>
|
|
</property>
|
|
</bean>
|
|
|
|
<bean id="global_debug" class="org.springframework.aop.interceptor.DebugInterceptor"/>
|
|
<bean id="global_performance" class="org.springframework.aop.interceptor.PerformanceMonitorInterceptor"/>
|
|
</programlisting></para>
|
|
</section>
|
|
</section>
|
|
|
|
<section id="aop-concise-proxy">
|
|
<title>Concise proxy definitions</title>
|
|
|
|
<para>Especially when defining transactional proxies, you may end up with
|
|
many similar proxy definitions. The use of parent and child bean
|
|
definitions, along with inner bean definitions, can result in much cleaner
|
|
and more concise proxy definitions.</para>
|
|
|
|
<para>First a parent, <emphasis>template</emphasis>, bean definition is
|
|
created for the proxy:</para>
|
|
|
|
<para><programlisting language="xml"><bean id="txProxyTemplate" abstract="true"
|
|
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
|
|
<property name="transactionManager" ref="transactionManager"/>
|
|
<property name="transactionAttributes">
|
|
<props>
|
|
<prop key="*">PROPAGATION_REQUIRED</prop>
|
|
</props>
|
|
</property>
|
|
</bean></programlisting></para>
|
|
|
|
<para>This will never be instantiated itself, so may actually be
|
|
incomplete. Then each proxy which needs to be created is just a child bean
|
|
definition, which wraps the target of the proxy as an inner bean
|
|
definition, since the target will never be used on its own
|
|
anyway.<programlisting language="xml"><bean id="myService" parent="txProxyTemplate">
|
|
<property name="target">
|
|
<bean class="org.springframework.samples.MyServiceImpl">
|
|
</bean>
|
|
</property>
|
|
</bean></programlisting></para>
|
|
|
|
<para>It is of course possible to override properties from the parent
|
|
template, such as in this case, the transaction propagation
|
|
settings:<programlisting language="xml"><bean id="mySpecialService" parent="txProxyTemplate">
|
|
<property name="target">
|
|
<bean class="org.springframework.samples.MySpecialServiceImpl">
|
|
</bean>
|
|
</property>
|
|
<property name="transactionAttributes">
|
|
<props>
|
|
<prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
|
|
<prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
|
|
<prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>
|
|
<prop key="store*">PROPAGATION_REQUIRED</prop>
|
|
</props>
|
|
</property>
|
|
</bean></programlisting></para>
|
|
|
|
<para>Note that in the example above, we have explicitly marked the parent
|
|
bean definition as <emphasis>abstract</emphasis> by using the
|
|
<emphasis>abstract</emphasis> attribute, as described <link
|
|
linkend="beans-child-bean-definitions">previously</link>, so that it may
|
|
not actually ever be instantiated. Application contexts (but not simple
|
|
bean factories) will by default pre-instantiate all singletons. It is
|
|
therefore important (at least for singleton beans) that if you have a
|
|
(parent) bean definition which you intend to use only as a template, and
|
|
this definition specifies a class, you must make sure to set the
|
|
<emphasis>abstract</emphasis> attribute to <emphasis>true</emphasis>,
|
|
otherwise the application context will actually try to pre-instantiate
|
|
it.</para>
|
|
</section>
|
|
|
|
<section id="aop-prog">
|
|
<title>Creating AOP proxies programmatically with the ProxyFactory</title>
|
|
|
|
<para>It's easy to create AOP proxies programmatically using Spring. This
|
|
enables you to use Spring AOP without dependency on Spring IoC.</para>
|
|
|
|
<para>The following listing shows creation of a proxy for a target object,
|
|
with one interceptor and one advisor. The interfaces implemented by the
|
|
target object will automatically be proxied:</para>
|
|
|
|
<para><programlisting language="java">ProxyFactory factory = new ProxyFactory(myBusinessInterfaceImpl);
|
|
factory.addAdvice(myMethodInterceptor);
|
|
factory.addAdvisor(myAdvisor);
|
|
MyBusinessInterface tb = (MyBusinessInterface) factory.getProxy();</programlisting></para>
|
|
|
|
<para>The first step is to construct an object of type
|
|
<literal>org.springframework.aop.framework.ProxyFactory</literal>. You can
|
|
create this with a target object, as in the above example, or specify the
|
|
interfaces to be proxied in an alternate constructor.</para>
|
|
|
|
<para>You can add advices (with interceptors as a specialized kind of advice)
|
|
and/or advisors, and manipulate them for the life of the ProxyFactory.
|
|
If you add an IntroductionInterceptionAroundAdvisor, you can cause the proxy
|
|
to implement additional interfaces.</para>
|
|
|
|
<para>There are also convenience methods on ProxyFactory (inherited from
|
|
<classname>AdvisedSupport</classname>) which allow you to add other advice
|
|
types such as before and throws advice. AdvisedSupport is the superclass
|
|
of both ProxyFactory and ProxyFactoryBean.</para>
|
|
|
|
<tip>
|
|
<para>Integrating AOP proxy creation with the IoC framework is best
|
|
practice in most applications. We recommend that you externalize
|
|
configuration from Java code with AOP, as in general.</para>
|
|
</tip>
|
|
</section>
|
|
|
|
<section id="aop-api-advised">
|
|
<title>Manipulating advised objects</title>
|
|
|
|
<para>However you create AOP proxies, you can manipulate them using the
|
|
<literal>org.springframework.aop.framework.Advised</literal> interface.
|
|
Any AOP proxy can be cast to this interface, whichever other interfaces it
|
|
implements. This interface includes the following methods:</para>
|
|
|
|
<programlisting language="java">Advisor[] getAdvisors();
|
|
|
|
void addAdvice(Advice advice) throws AopConfigException;
|
|
|
|
void addAdvice(int pos, Advice advice)
|
|
throws AopConfigException;
|
|
|
|
void addAdvisor(Advisor advisor) throws AopConfigException;
|
|
|
|
void addAdvisor(int pos, Advisor advisor) throws AopConfigException;
|
|
|
|
int indexOf(Advisor advisor);
|
|
|
|
boolean removeAdvisor(Advisor advisor) throws AopConfigException;
|
|
|
|
void removeAdvisor(int index) throws AopConfigException;
|
|
|
|
boolean replaceAdvisor(Advisor a, Advisor b) throws AopConfigException;
|
|
|
|
boolean isFrozen();</programlisting>
|
|
|
|
<para>The <literal>getAdvisors()</literal> method will return an Advisor
|
|
for every advisor, interceptor or other advice type that has been added to
|
|
the factory. If you added an Advisor, the returned advisor at this index
|
|
will be the object that you added. If you added an interceptor or other
|
|
advice type, Spring will have wrapped this in an advisor with a pointcut
|
|
that always returns true. Thus if you added a
|
|
<literal>MethodInterceptor</literal>, the advisor returned for this index
|
|
will be an <literal>DefaultPointcutAdvisor</literal> returning your
|
|
<literal>MethodInterceptor</literal> and a pointcut that matches all
|
|
classes and methods.</para>
|
|
|
|
<para>The <literal>addAdvisor()</literal> methods can be used to add any
|
|
Advisor. Usually the advisor holding pointcut and advice will be the
|
|
generic <literal>DefaultPointcutAdvisor</literal>, which can be used with
|
|
any advice or pointcut (but not for introductions).</para>
|
|
|
|
<para>By default, it's possible to add or remove advisors or interceptors
|
|
even once a proxy has been created. The only restriction is that it's
|
|
impossible to add or remove an introduction advisor, as existing proxies
|
|
from the factory will not show the interface change. (You can obtain a new
|
|
proxy from the factory to avoid this problem.)</para>
|
|
|
|
<para>A simple example of casting an AOP proxy to the
|
|
<literal>Advised</literal> interface and examining and manipulating its
|
|
advice:</para>
|
|
|
|
<para><programlisting language="java">Advised advised = (Advised) myObject;
|
|
Advisor[] advisors = advised.getAdvisors();
|
|
int oldAdvisorCount = advisors.length;
|
|
System.out.println(oldAdvisorCount + " advisors");
|
|
|
|
// Add an advice like an interceptor without a pointcut
|
|
// Will match all proxied methods
|
|
// Can use for interceptors, before, after returning or throws advice
|
|
advised.addAdvice(new DebugInterceptor());
|
|
|
|
// Add selective advice using a pointcut
|
|
advised.addAdvisor(new DefaultPointcutAdvisor(mySpecialPointcut, myAdvice));
|
|
|
|
assertEquals("Added two advisors",
|
|
oldAdvisorCount + 2, advised.getAdvisors().length);</programlisting></para>
|
|
|
|
<note>
|
|
<para>It's questionable whether it's advisable (no pun intended) to
|
|
modify advice on a business object in production, although there are no
|
|
doubt legitimate usage cases. However, it can be very useful in
|
|
development: for example, in tests. I have sometimes found it very
|
|
useful to be able to add test code in the form of an interceptor or
|
|
other advice, getting inside a method invocation I want to test. (For
|
|
example, the advice can get inside a transaction created for that
|
|
method: for example, to run SQL to check that a database was correctly
|
|
updated, before marking the transaction for roll back.)</para>
|
|
</note>
|
|
|
|
<para>Depending on how you created the proxy, you can usually set a
|
|
<literal>frozen</literal> flag, in which case the
|
|
<literal>Advised</literal> <literal>isFrozen()</literal> method will
|
|
return true, and any attempts to modify advice through addition or removal
|
|
will result in an <literal>AopConfigException</literal>. The ability to
|
|
freeze the state of an advised object is useful in some cases, for
|
|
example, to prevent calling code removing a security interceptor. It may
|
|
also be used in Spring 1.1 to allow aggressive optimization if runtime
|
|
advice modification is known not to be required.</para>
|
|
</section>
|
|
|
|
<section id="aop-autoproxy">
|
|
<title>Using the "autoproxy" facility</title>
|
|
|
|
<para>So far we've considered explicit creation of AOP proxies using a
|
|
<literal>ProxyFactoryBean</literal> or similar factory bean.</para>
|
|
|
|
<para>Spring also allows us to use "autoproxy" bean definitions, which can
|
|
automatically proxy selected bean definitions. This is built on Spring
|
|
"bean post processor" infrastructure, which enables modification of any
|
|
bean definition as the container loads.</para>
|
|
|
|
<para>In this model, you set up some special bean definitions in your XML
|
|
bean definition file to configure the auto proxy infrastructure. This
|
|
allows you just to declare the targets eligible for autoproxying: you
|
|
don't need to use <literal>ProxyFactoryBean</literal>.</para>
|
|
|
|
<para>There are two ways to do this:</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>Using an autoproxy creator that refers to specific beans in the
|
|
current context.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>A special case of autoproxy creation that deserves to be
|
|
considered separately; autoproxy creation driven by source-level
|
|
metadata attributes.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<section id="aop-autoproxy-choices">
|
|
<title>Autoproxy bean definitions</title>
|
|
|
|
<para>The <literal>org.springframework.aop.framework.autoproxy</literal>
|
|
package provides the following standard autoproxy creators.</para>
|
|
|
|
<section id="aop-api-autoproxy">
|
|
<title>BeanNameAutoProxyCreator</title>
|
|
|
|
<para>The <literal>BeanNameAutoProxyCreator</literal> class is a
|
|
<literal>BeanPostProcessor</literal> that automatically creates AOP
|
|
proxies for beans with names matching literal values or
|
|
wildcards.</para>
|
|
|
|
<para><programlisting language="xml"><bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
|
|
<property name="beanNames" value="jdk*,onlyJdk"/>
|
|
<property name="interceptorNames">
|
|
<list>
|
|
<value>myInterceptor</value>
|
|
</list>
|
|
</property>
|
|
</bean></programlisting></para>
|
|
|
|
<para>As with <literal>ProxyFactoryBean</literal>, there is an
|
|
<literal>interceptorNames</literal> property rather than a list of
|
|
interceptors, to allow correct behavior for prototype advisors. Named
|
|
"interceptors" can be advisors or any advice type.</para>
|
|
|
|
<para>As with auto proxying in general, the main point of using
|
|
<literal>BeanNameAutoProxyCreator</literal> is to apply the same
|
|
configuration consistently to multiple objects, with minimal volume of
|
|
configuration. It is a popular choice for applying declarative
|
|
transactions to multiple objects.</para>
|
|
|
|
<para>Bean definitions whose names match, such as "jdkMyBean" and
|
|
"onlyJdk" in the above example, are plain old bean definitions with
|
|
the target class. An AOP proxy will be created automatically by the
|
|
<literal>BeanNameAutoProxyCreator</literal>. The same advice will be
|
|
applied to all matching beans. Note that if advisors are used (rather
|
|
than the interceptor in the above example), the pointcuts may apply
|
|
differently to different beans.</para>
|
|
</section>
|
|
|
|
<section id="aop-api-autoproxy-default">
|
|
<title>DefaultAdvisorAutoProxyCreator</title>
|
|
|
|
<para>A more general and extremely powerful auto proxy creator is
|
|
<literal>DefaultAdvisorAutoProxyCreator</literal>. This will
|
|
automagically apply eligible advisors in the current context, without
|
|
the need to include specific bean names in the autoproxy advisor's
|
|
bean definition. It offers the same merit of consistent configuration
|
|
and avoidance of duplication as
|
|
<literal>BeanNameAutoProxyCreator</literal>.</para>
|
|
|
|
<para>Using this mechanism involves:</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>Specifying a
|
|
<literal>DefaultAdvisorAutoProxyCreator</literal> bean
|
|
definition.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Specifying any number of Advisors in the same or related
|
|
contexts. Note that these <emphasis>must</emphasis> be Advisors,
|
|
not just interceptors or other advices. This is necessary because
|
|
there must be a pointcut to evaluate, to check the eligibility of
|
|
each advice to candidate bean definitions.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>The <literal>DefaultAdvisorAutoProxyCreator</literal> will
|
|
automatically evaluate the pointcut contained in each advisor, to see
|
|
what (if any) advice it should apply to each business object (such as
|
|
"businessObject1" and "businessObject2" in the example).</para>
|
|
|
|
<para>This means that any number of advisors can be applied
|
|
automatically to each business object. If no pointcut in any of the
|
|
advisors matches any method in a business object, the object will not
|
|
be proxied. As bean definitions are added for new business objects,
|
|
they will automatically be proxied if necessary.</para>
|
|
|
|
<para>Autoproxying in general has the advantage of making it
|
|
impossible for callers or dependencies to obtain an un-advised object.
|
|
Calling getBean("businessObject1") on this ApplicationContext will
|
|
return an AOP proxy, not the target business object. (The "inner bean"
|
|
idiom shown earlier also offers this benefit.)</para>
|
|
|
|
<para><programlisting language="xml"><bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
|
|
|
|
<bean class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor">
|
|
<property name="transactionInterceptor" ref="transactionInterceptor"/>
|
|
</bean>
|
|
|
|
<bean id="customAdvisor" class="com.mycompany.MyAdvisor"/>
|
|
|
|
<bean id="businessObject1" class="com.mycompany.BusinessObject1">
|
|
<!-- Properties omitted -->
|
|
</bean>
|
|
|
|
<bean id="businessObject2" class="com.mycompany.BusinessObject2"/>
|
|
</programlisting></para>
|
|
|
|
<para>The <literal>DefaultAdvisorAutoProxyCreator</literal> is very
|
|
useful if you want to apply the same advice consistently to many
|
|
business objects. Once the infrastructure definitions are in place,
|
|
you can simply add new business objects without including specific
|
|
proxy configuration. You can also drop in additional aspects very
|
|
easily - for example, tracing or performance monitoring aspects - with
|
|
minimal change to configuration.</para>
|
|
|
|
<para>The DefaultAdvisorAutoProxyCreator offers support for filtering
|
|
(using a naming convention so that only certain advisors are
|
|
evaluated, allowing use of multiple, differently configured,
|
|
AdvisorAutoProxyCreators in the same factory) and ordering. Advisors
|
|
can implement the <literal>org.springframework.core.Ordered</literal>
|
|
interface to ensure correct ordering if this is an issue. The
|
|
TransactionAttributeSourceAdvisor used in the above example has a
|
|
configurable order value; the default setting is unordered.</para>
|
|
</section>
|
|
|
|
<section id="aop-api-autoproxy-abstract">
|
|
<title>AbstractAdvisorAutoProxyCreator</title>
|
|
|
|
<para>This is the superclass of DefaultAdvisorAutoProxyCreator. You
|
|
can create your own autoproxy creators by subclassing this class, in
|
|
the unlikely event that advisor definitions offer insufficient
|
|
customization to the behavior of the framework
|
|
<literal>DefaultAdvisorAutoProxyCreator</literal>.</para>
|
|
</section>
|
|
</section>
|
|
|
|
<section id="aop-autoproxy-metadata">
|
|
<title>Using metadata-driven auto-proxying</title>
|
|
|
|
<para>A particularly important type of autoproxying is driven by
|
|
metadata. This produces a similar programming model to .NET
|
|
<literal>ServicedComponents</literal>. Instead of using XML deployment
|
|
descriptors as in EJB, configuration for transaction management and
|
|
other enterprise services is held in source-level attributes.</para>
|
|
|
|
<para>In this case, you use the
|
|
<literal>DefaultAdvisorAutoProxyCreator</literal>, in combination with
|
|
Advisors that understand metadata attributes. The metadata specifics are
|
|
held in the pointcut part of the candidate advisors, rather than in the
|
|
autoproxy creation class itself.</para>
|
|
|
|
<para>This is really a special case of the
|
|
<literal>DefaultAdvisorAutoProxyCreator</literal>, but deserves
|
|
consideration on its own. (The metadata-aware code is in the pointcuts
|
|
contained in the advisors, not the AOP framework itself.)</para>
|
|
|
|
<para>The <literal>/attributes</literal> directory of the JPetStore
|
|
sample application shows the use of attribute-driven autoproxying. In
|
|
this case, there's no need to use the
|
|
<literal>TransactionProxyFactoryBean</literal>. Simply defining
|
|
transactional attributes on business objects is sufficient, because of
|
|
the use of metadata-aware pointcuts. The bean definitions include the
|
|
following code, in <literal>/WEB-INF/declarativeServices.xml</literal>.
|
|
Note that this is generic, and can be used outside the JPetStore:</para>
|
|
|
|
<para><programlisting language="xml"><bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
|
|
|
|
<bean class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor">
|
|
<property name="transactionInterceptor" ref="transactionInterceptor"/>
|
|
</bean>
|
|
|
|
<bean id="transactionInterceptor"
|
|
class="org.springframework.transaction.interceptor.TransactionInterceptor">
|
|
<property name="transactionManager" ref="transactionManager"/>
|
|
<property name="transactionAttributeSource">
|
|
<bean class="org.springframework.transaction.interceptor.AttributesTransactionAttributeSource">
|
|
<property name="attributes" ref="attributes"/>
|
|
</bean>
|
|
</property>
|
|
</bean>
|
|
|
|
<bean id="attributes" class="org.springframework.metadata.commons.CommonsAttributes"/></programlisting></para>
|
|
|
|
<para>The <literal>DefaultAdvisorAutoProxyCreator</literal> bean
|
|
definition (the name is not significant, hence it can even be omitted)
|
|
will pick up all eligible pointcuts in the current application context.
|
|
In this case, the "transactionAdvisor" bean definition, of type
|
|
<literal>TransactionAttributeSourceAdvisor</literal>, will apply to
|
|
classes or methods carrying a transaction attribute. The
|
|
TransactionAttributeSourceAdvisor depends on a TransactionInterceptor,
|
|
via constructor dependency. The example resolves this via autowiring.
|
|
The <literal>AttributesTransactionAttributeSource</literal> depends on
|
|
an implementation of the
|
|
<literal>org.springframework.metadata.Attributes</literal> interface. In
|
|
this fragment, the "attributes" bean satisfies this, using the Jakarta
|
|
Commons Attributes API to obtain attribute information. (The application
|
|
code must have been compiled using the Commons Attributes compilation
|
|
task.)</para>
|
|
|
|
<para>The <literal>/annotation</literal> directory of the JPetStore
|
|
sample application contains an analogous example for auto-proxying
|
|
driven by JDK 1.5+ annotations. The following configuration enables
|
|
automatic detection of Spring's <literal>Transactional</literal>
|
|
annotation, leading to implicit proxies for beans containing that
|
|
annotation:</para>
|
|
|
|
<para><programlisting language="xml"><bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
|
|
|
|
<bean class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor">
|
|
<property name="transactionInterceptor" ref="transactionInterceptor"/>
|
|
</bean>
|
|
|
|
<bean id="transactionInterceptor"
|
|
class="org.springframework.transaction.interceptor.TransactionInterceptor">
|
|
<property name="transactionManager" ref="transactionManager"/>
|
|
<property name="transactionAttributeSource">
|
|
<bean class="org.springframework.transaction.annotation.AnnotationTransactionAttributeSource"/>
|
|
</property>
|
|
</bean></programlisting></para>
|
|
|
|
<para>The <literal>TransactionInterceptor</literal> defined here depends
|
|
on a <literal>PlatformTransactionManager</literal> definition, which is
|
|
not included in this generic file (although it could be) because it will
|
|
be specific to the application's transaction requirements (typically
|
|
JTA, as in this example, or Hibernate, JDO or JDBC):</para>
|
|
|
|
<programlisting language="xml"><bean id="transactionManager"
|
|
class="org.springframework.transaction.jta.JtaTransactionManager"/></programlisting>
|
|
|
|
<tip>
|
|
<para>If you require only declarative transaction management, using
|
|
these generic XML definitions will result in Spring automatically
|
|
proxying all classes or methods with transaction attributes. You won't
|
|
need to work directly with AOP, and the programming model is similar
|
|
to that of .NET ServicedComponents.</para>
|
|
</tip>
|
|
|
|
<para>This mechanism is extensible. It's possible to do autoproxying
|
|
based on custom attributes. You need to:</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>Define your custom attribute.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Specify an Advisor with the necessary advice, including a
|
|
pointcut that is triggered by the presence of the custom attribute
|
|
on a class or method. You may be able to use an existing advice,
|
|
merely implementing a static pointcut that picks up the custom
|
|
attribute.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>It's possible for such advisors to be unique to each advised class
|
|
(for example, mixins): they simply need to be defined as prototype,
|
|
rather than singleton, bean definitions. For example, the
|
|
<literal>LockMixin</literal> introduction interceptor from the Spring
|
|
test suite, shown above, could be used in conjunction with an
|
|
attribute-driven pointcut to target a mixin, as shown here. We use the
|
|
generic <literal>DefaultPointcutAdvisor</literal>, configured using
|
|
JavaBean properties:</para>
|
|
|
|
<para><programlisting language="xml"><bean id="lockMixin" class="org.springframework.aop.LockMixin"
|
|
scope="prototype"/>
|
|
|
|
<bean id="lockableAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"
|
|
scope="prototype">
|
|
<property name="pointcut" ref="myAttributeAwarePointcut"/>
|
|
<property name="advice" ref="lockMixin"/>
|
|
</bean>
|
|
|
|
<bean id="anyBean" class="anyclass" ...</programlisting></para>
|
|
|
|
<para>If the attribute aware pointcut matches any methods in the
|
|
<literal>anyBean</literal> or other bean definitions, the mixin will be
|
|
applied. Note that both <literal>lockMixin</literal> and
|
|
<literal>lockableAdvisor</literal> definitions are prototypes. The
|
|
<literal>myAttributeAwarePointcut</literal> pointcut can be a singleton
|
|
definition, as it doesn't hold state for individual advised
|
|
objects.</para>
|
|
</section>
|
|
</section>
|
|
|
|
<section id="aop-targetsource">
|
|
<title>Using TargetSources</title>
|
|
|
|
<para>Spring offers the concept of a <emphasis>TargetSource</emphasis>,
|
|
expressed in the <literal>org.springframework.aop.TargetSource</literal>
|
|
interface. This interface is responsible for returning the "target object"
|
|
implementing the join point. The <literal>TargetSource</literal>
|
|
implementation is asked for a target instance each time the AOP proxy
|
|
handles a method invocation.</para>
|
|
|
|
<para>Developers using Spring AOP don't normally need to work directly
|
|
with TargetSources, but this provides a powerful means of supporting
|
|
pooling, hot swappable and other sophisticated targets. For example, a
|
|
pooling TargetSource can return a different target instance for each
|
|
invocation, using a pool to manage instances.</para>
|
|
|
|
<para>If you do not specify a TargetSource, a default implementation is
|
|
used that wraps a local object. The same target is returned for each
|
|
invocation (as you would expect).</para>
|
|
|
|
<para>Let's look at the standard target sources provided with Spring, and
|
|
how you can use them.</para>
|
|
|
|
<tip>
|
|
<para>When using a custom target source, your target will usually need
|
|
to be a prototype rather than a singleton bean definition. This allows
|
|
Spring to create a new target instance when required.</para>
|
|
</tip>
|
|
|
|
<section id="aop-ts-swap">
|
|
<title>Hot swappable target sources</title>
|
|
|
|
<para>The
|
|
<literal>org.springframework.aop.target.HotSwappableTargetSource</literal>
|
|
exists to allow the target of an AOP proxy to be switched while allowing
|
|
callers to keep their references to it.</para>
|
|
|
|
<para>Changing the target source's target takes effect immediately. The
|
|
<literal>HotSwappableTargetSource</literal> is threadsafe.</para>
|
|
|
|
<para>You can change the target via the <literal>swap()</literal> method
|
|
on HotSwappableTargetSource as follows:</para>
|
|
|
|
<para><programlisting language="java">HotSwappableTargetSource swapper =
|
|
(HotSwappableTargetSource) beanFactory.getBean("swapper");
|
|
Object oldTarget = swapper.swap(newTarget);</programlisting></para>
|
|
|
|
<para>The XML definitions required look as follows:</para>
|
|
|
|
<para><programlisting language="xml"><bean id="initialTarget" class="mycompany.OldTarget"/>
|
|
|
|
<bean id="swapper" class="org.springframework.aop.target.HotSwappableTargetSource">
|
|
<constructor-arg ref="initialTarget"/>
|
|
</bean>
|
|
|
|
<bean id="swappable" class="org.springframework.aop.framework.ProxyFactoryBean">
|
|
<property name="targetSource" ref="swapper"/>
|
|
</bean></programlisting></para>
|
|
|
|
<para>The above <literal>swap()</literal> call changes the target of the
|
|
swappable bean. Clients who hold a reference to that bean will be
|
|
unaware of the change, but will immediately start hitting the new
|
|
target.</para>
|
|
|
|
<para>Although this example doesn't add any advice - and it's not
|
|
necessary to add advice to use a <literal>TargetSource</literal> - of
|
|
course any <literal>TargetSource</literal> can be used in conjunction
|
|
with arbitrary advice.</para>
|
|
</section>
|
|
|
|
<section id="aop-ts-pool">
|
|
<title>Pooling target sources</title>
|
|
|
|
<para>Using a pooling target source provides a similar programming model
|
|
to stateless session EJBs, in which a pool of identical instances is
|
|
maintained, with method invocations going to free objects in the
|
|
pool.</para>
|
|
|
|
<para>A crucial difference between Spring pooling and SLSB pooling is
|
|
that Spring pooling can be applied to any POJO. As with Spring in
|
|
general, this service can be applied in a non-invasive way.</para>
|
|
|
|
<para>Spring provides out-of-the-box support for Jakarta Commons Pool
|
|
1.3, which provides a fairly efficient pooling implementation. You'll
|
|
need the commons-pool Jar on your application's classpath to use this
|
|
feature. It's also possible to subclass
|
|
<literal>org.springframework.aop.target.AbstractPoolingTargetSource</literal>
|
|
to support any other pooling API.</para>
|
|
|
|
<para>Sample configuration is shown below:</para>
|
|
|
|
<para><programlisting language="xml"><bean id="businessObjectTarget" class="com.mycompany.MyBusinessObject"
|
|
scope="prototype">
|
|
... properties omitted
|
|
</bean>
|
|
|
|
<bean id="poolTargetSource" class="org.springframework.aop.target.CommonsPoolTargetSource">
|
|
<property name="targetBeanName" value="businessObjectTarget"/>
|
|
<property name="maxSize" value="25"/>
|
|
</bean>
|
|
|
|
<bean id="businessObject" class="org.springframework.aop.framework.ProxyFactoryBean">
|
|
<property name="targetSource" ref="poolTargetSource"/>
|
|
<property name="interceptorNames" value="myInterceptor"/>
|
|
</bean></programlisting></para>
|
|
|
|
<para>Note that the target object - "businessObjectTarget" in the
|
|
example - <emphasis>must</emphasis> be a prototype. This allows the
|
|
<literal>PoolingTargetSource</literal> implementation to create new
|
|
instances of the target to grow the pool as necessary. See the javadoc
|
|
for <literal>AbstractPoolingTargetSource</literal> and the concrete
|
|
subclass you wish to use for information about its properties: "maxSize"
|
|
is the most basic, and always guaranteed to be present.</para>
|
|
|
|
<para>In this case, "myInterceptor" is the name of an interceptor that
|
|
would need to be defined in the same IoC context. However, it isn't
|
|
necessary to specify interceptors to use pooling. If you want only
|
|
pooling, and no other advice, don't set the interceptorNames property at
|
|
all.</para>
|
|
|
|
<para>It's possible to configure Spring so as to be able to cast any
|
|
pooled object to the
|
|
<literal>org.springframework.aop.target.PoolingConfig</literal>
|
|
interface, which exposes information about the configuration and current
|
|
size of the pool through an introduction. You'll need to define an
|
|
advisor like this:</para>
|
|
|
|
<para><programlisting language="xml"><bean id="poolConfigAdvisor" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
|
|
<property name="targetObject" ref="poolTargetSource"/>
|
|
<property name="targetMethod" value="getPoolingConfigMixin"/>
|
|
</bean></programlisting></para>
|
|
|
|
<para>This advisor is obtained by calling a convenience method on the
|
|
<literal>AbstractPoolingTargetSource</literal> class, hence the use of
|
|
MethodInvokingFactoryBean. This advisor's name ("poolConfigAdvisor"
|
|
here) must be in the list of interceptors names in the ProxyFactoryBean
|
|
exposing the pooled object.</para>
|
|
|
|
<para>The cast will look as follows:</para>
|
|
|
|
<programlisting language="java">PoolingConfig conf = (PoolingConfig) beanFactory.getBean("businessObject");
|
|
System.out.println("Max pool size is " + conf.getMaxSize());</programlisting>
|
|
|
|
<note>
|
|
<para>Pooling stateless service objects is not usually necessary. We
|
|
don't believe it should be the default choice, as most stateless
|
|
objects are naturally thread safe, and instance pooling is problematic
|
|
if resources are cached.</para>
|
|
</note>
|
|
|
|
<para>Simpler pooling is available using autoproxying. It's possible to
|
|
set the TargetSources used by any autoproxy creator.</para>
|
|
</section>
|
|
|
|
<section id="aop-ts-prototype">
|
|
<title>Prototype target sources</title>
|
|
|
|
<para>Setting up a "prototype" target source is similar to a pooling
|
|
TargetSource. In this case, a new instance of the target will be created
|
|
on every method invocation. Although the cost of creating a new object
|
|
isn't high in a modern JVM, the cost of wiring up the new object
|
|
(satisfying its IoC dependencies) may be more expensive. Thus you
|
|
shouldn't use this approach without very good reason.</para>
|
|
|
|
<para>To do this, you could modify the
|
|
<literal>poolTargetSource</literal> definition shown above as follows.
|
|
(I've also changed the name, for clarity.)</para>
|
|
|
|
<programlisting language="xml"><bean id="prototypeTargetSource" class="org.springframework.aop.target.PrototypeTargetSource">
|
|
<property name="targetBeanName" ref="businessObjectTarget"/>
|
|
</bean></programlisting>
|
|
|
|
<para>There's only one property: the name of the target bean.
|
|
Inheritance is used in the TargetSource implementations to ensure
|
|
consistent naming. As with the pooling target source, the target bean
|
|
must be a prototype bean definition.</para>
|
|
</section>
|
|
|
|
<section id="aop-ts-threadlocal">
|
|
<title><classname>ThreadLocal</classname> target sources</title>
|
|
|
|
<para><classname>ThreadLocal</classname> target sources are useful if
|
|
you need an object to be created for each incoming request (per thread
|
|
that is). The concept of a <classname>ThreadLocal</classname> provide a
|
|
JDK-wide facility to transparently store resource alongside a thread.
|
|
Setting up a <classname>ThreadLocalTargetSource</classname> is pretty
|
|
much the same as was explained for the other types of target
|
|
source:</para>
|
|
|
|
<programlisting language="xml"><bean id="threadlocalTargetSource" class="org.springframework.aop.target.ThreadLocalTargetSource">
|
|
<property name="targetBeanName" value="businessObjectTarget"/>
|
|
</bean></programlisting>
|
|
|
|
<note>
|
|
<para>ThreadLocals come with serious issues (potentially resulting in
|
|
memory leaks) when incorrectly using them in a multi-threaded and
|
|
multi-classloader environments. One should always consider wrapping a
|
|
threadlocal in some other class and never directly use the
|
|
<classname>ThreadLocal</classname> itself (except of course in the
|
|
wrapper class). Also, one should always remember to correctly set and
|
|
unset (where the latter simply involved a call to
|
|
<literal>ThreadLocal.set(null)</literal>) the resource local to the
|
|
thread. Unsetting should be done in any case since not unsetting it
|
|
might result in problematic behavior. Spring's ThreadLocal support
|
|
does this for you and should always be considered in favor of using
|
|
ThreadLocals without other proper handling code.</para>
|
|
</note>
|
|
</section>
|
|
</section>
|
|
|
|
<section id="aop-extensibility">
|
|
<title>Defining new <interfacename>Advice</interfacename> types</title>
|
|
|
|
<para>Spring AOP is designed to be extensible. While the interception
|
|
implementation strategy is presently used internally, it is possible to
|
|
support arbitrary advice types in addition to the out-of-the-box
|
|
interception around advice, before, throws advice and after returning
|
|
advice.</para>
|
|
|
|
<para>The <literal>org.springframework.aop.framework.adapter</literal>
|
|
package is an SPI package allowing support for new custom advice types to
|
|
be added without changing the core framework. The only constraint on a
|
|
custom <interfacename>Advice</interfacename> type is that it must
|
|
implement the <interfacename>org.aopalliance.aop.Advice</interfacename>
|
|
tag interface.</para>
|
|
|
|
<para>Please refer to the
|
|
<literal>org.springframework.aop.framework.adapter</literal> package's
|
|
Javadocs for further information.</para>
|
|
</section>
|
|
|
|
<section id="aop-api-resources">
|
|
<title>Further resources</title>
|
|
|
|
<para>Please refer to the Spring sample applications for further examples
|
|
of Spring AOP:</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>The JPetStore's default configuration illustrates the use of the
|
|
<classname>TransactionProxyFactoryBean</classname> for declarative
|
|
transaction management.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>The <literal>/attributes</literal> directory of the JPetStore
|
|
illustrates the use of attribute-driven declarative transaction
|
|
management.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</section>
|
|
</chapter>
|