Files
spring-net/doc/reference/src/aop.xml
sbohlen 9788fd3580 SPRNET-1407
Updated copyright notices in docs.
2010-12-11 20:35:49 +00:00

2569 lines
110 KiB
XML

<?xml version="1.0" encoding="UTF-8"?>
<!--
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-->
<chapter version="5" xml:id="aop" xmlns="http://docbook.org/ns/docbook"
xmlns:ns5="http://www.w3.org/1998/Math/MathML"
xmlns:ns4="http://www.w3.org/2000/svg"
xmlns:ns3="http://www.w3.org/1999/xhtml"
xmlns:ns2="http://www.w3.org/1999/xlink"
xmlns:ns="http://docbook.org/ns/docbook">
<title>Aspect Oriented Programming with Spring.NET</title>
<sect1 xml:id="aop-introduction-concepts">
<title>Introduction</title>
<para><emphasis>Aspect-Oriented Programming</emphasis>
(<emphasis>AOP</emphasis>) complements OOP by providing another way of
thinking about program structure. Whereas OO decomposes applications into
a hierarchy of objects, AOP decomposes programs into
<emphasis>aspects</emphasis> or <emphasis>concerns</emphasis>. This
enables the modularization of concerns such as transaction management that
would otherwise cut across multiple objects (such concerns are often
termed <emphasis>crosscutting</emphasis> concerns).</para>
<para>One of the key components of Spring.NET is the <emphasis>AOP
framework</emphasis>. While the Spring.NET IoC container does not depend
on AOP, meaning you don't need to use AOP if you don't want to, AOP
complements Spring.NET IoC to provide a very capable middleware
solution.</para>
<para>AOP is used in Spring.NET:</para>
<itemizedlist>
<listitem>
<para>To provide declarative enterprise services, especially as a
replacement for COM+ declarative services. The most important such
service is <emphasis>declarative transaction management</emphasis>,
which builds on Spring.NET's transaction abstraction. This
functionality is planed for an upcoming release of Spring.NET</para>
</listitem>
<listitem>
<para>To allow users to implement custom aspects, complementing their
use of OOP with AOP.</para>
</listitem>
</itemizedlist>
<para>Thus you can view Spring.NET AOP as either an enabling technology
that allows Spring.NET to provide declarative transaction management
without COM+; or use the full power of the Spring.NET AOP framework to
implement custom aspects.</para>
<para>For those who would like to hit the ground running and start
exploring how to use Spring's AOP functionality, head on over to <xref
linkend="aop-quickstart" />.</para>
<sect2 xml:id="aop-introduction-defn">
<title>AOP concepts</title>
<para>Let us begin by defining some central AOP concepts. These terms
are not Spring.NET-specific. Unfortunately, AOP terminology is not
particularly intuitive. However, it would be even more confusing if
Spring.NET used its own terminology.</para>
<itemizedlist>
<listitem>
<para><emphasis>Aspect</emphasis>: A modularization of a concern for
which the implementation might otherwise cut across multiple
objects. Transaction management is a good example of a crosscutting
concern in enterprise applications. Aspects are implemented using
Spring.NET as Advisors or interceptors.</para>
</listitem>
<listitem>
<para><emphasis>Joinpoint</emphasis>: Point during the execution of
a program, such as a method invocation or a particular exception
being thrown.</para>
</listitem>
<listitem>
<para><emphasis>Advice</emphasis>: Action taken by the AOP framework
at a particular joinpoint. Different types of advice include
"around," "before" and "throws" advice. Advice types are discussed
below. Many AOP frameworks, including Spring.NET, model an advice as
an <emphasis>interceptor</emphasis>, maintaining a chain of
interceptors "around" the joinpoint.</para>
</listitem>
<listitem>
<para><emphasis>Pointcut</emphasis>: A set of joinpoints specifying
when an advice should fire. An AOP framework must allow developers
to specify pointcuts: for example, using regular expressions.</para>
</listitem>
<listitem>
<para><emphasis>Introduction</emphasis>: Adding methods or fields to
an advised class. Spring.NET allows you to introduce new interfaces
to any advised object. For example, you could use an introduction to
make any object implement an <literal>IAuditable</literal>
interface, to simplify the tracking of changes to an object's
state.</para>
</listitem>
<listitem>
<para><emphasis>Target object</emphasis>: Object containing the
joinpoint. Also referred to as <emphasis>advised</emphasis> or
<emphasis>proxied</emphasis> object.</para>
</listitem>
<listitem>
<para><emphasis>AOP proxy</emphasis>: Object created by the AOP
framework, including advice. In Spring.NET, an AOP proxy is a
dynamic proxy that uses IL code generated at runtime.</para>
</listitem>
<listitem>
<para><emphasis>Weaving</emphasis>: Assembling aspects to create an
advised object. This can be done at compile time (using the
Gripper-Loom.NET compiler, for example), or at runtime. Spring.NET
performs weaving at runtime.</para>
</listitem>
</itemizedlist>
<para>Different advice types include:</para>
<itemizedlist>
<listitem>
<para><emphasis>Around advice</emphasis>: Advice that surrounds a
joinpoint such as a method invocation. This is the most powerful
kind of advice. Around advice will perform custom behaviour before
and after the method invocation. They are responsible for choosing
whether to proceed to the joinpoint or to shortcut executing by
returning their own return value or throwing an exception.</para>
</listitem>
<listitem>
<para><emphasis>Before advice</emphasis>: Advice that executes
before a joinpoint, but which does not have the ability to prevent
execution flow proceeding to the joinpoint (unless it throws an
exception).</para>
</listitem>
<listitem>
<para><emphasis>Throws advice</emphasis>: Advice to be executed if a
method throws an exception. Spring.NET provides strongly typed
throws advice, so you can write code that catches the exception (and
subclasses) you're interested in, without needing to cast from
Exception.</para>
</listitem>
<listitem>
<para><emphasis>After returning advice</emphasis>: Advice to be
executed after a joinpoint completes normally: for example, if a
method returns without throwing an exception.</para>
</listitem>
</itemizedlist>
<para>Spring.NET provides a full range of advice types. We recommend
that you use the least powerful advice type that can implement the
required behaviour. For example, if you need only to update a cache with
the return value of a method, you are better off implementing an after
returning advice than an around advice, although an around advice can
accomplish the same thing. Using the most specific advice type provides
a simpler programming model with less potential for errors. For example,
you don't need to invoke the <literal>proceed()</literal> method on the
<literal>IMethodInvocation</literal> used for around advice, and hence
can't fail to invoke it.</para>
<para>The pointcut concept is the key to AOP, distinguishing AOP from
older technologies offering interception. Pointcuts enable advice to be
targeted independently of the OO hierarchy. For example, an around
advice providing declarative transaction management can be applied to a
set of methods spanning multiple objects. Thus pointcuts provide the
structural element of AOP.</para>
</sect2>
<sect2 xml:id="aop-introduction-Spring.NET-defn">
<title>Spring.NET AOP capabilities</title>
<para>Spring.NET AOP is implemented in pure C#. There is no need for a
special compilation process - all weaving is done at runtime. Spring.NET
AOP does not need to control or modify the way in which assemblies are
loaded, nor does it rely on unmanaged APIs, and is thus suitable for use
in any CLR environment.</para>
<para>Spring.NET currently supports interception of method invocations.
Field interception is not implemented, although support for field
interception could be added without breaking the core Spring.NET AOP
APIs.</para>
<remark>Field interception arguably violates OO encapsulation. We don't
believe it is wise in application development.</remark>
<para>Spring.NET provides classes to represent pointcuts and different
advice types. Spring.NET uses the term <emphasis>advisor</emphasis> for
an object representing an aspect, including both an advice and a
pointcut targeting it to specific joinpoints.</para>
<para>Different advice types are <literal>IMethodInterceptor</literal>
(from the AOP Alliance interception API); and the advice interfaces
defined in the <literal>Spring.Aop</literal> namespace. All advices must
implement the <literal>AopAlliance.Aop.IAdvice</literal> tag interface.
Advices supported out the box are <literal>IMethodInterceptor</literal>
; <literal>IThrowsAdvice</literal>; <literal>IBeforeAdvice</literal>;
and <literal>IAfterReturningAdvice</literal>. We'll discuss advice types
in detail below.</para>
<para>Spring.NET provides a .NET translation of the Java interfaces
defined by the <ulink url="http://aopalliance.sourceforge.NET/">
<emphasis>AOP Alliance</emphasis>
</ulink>. Around advice must implement the AOP Alliance
<literal>AopAlliance.Interceptr.IMethodInterceptor</literal> interface.
Whilst there is wide support for the AOP Alliance in Java, Spring.NET is
currently the only .NET AOP framework that makes use of these
interfaces. In the short term, this will provide a consistent
programming model for those doing development in both .NET and Java, and
in the longer term, we hope to see more .NET projects adopt the AOP
Alliance interfaces.</para>
<remark>The aim of Spring.NET AOP support is not to provide a
comprehensive AOP implementation on par with the functionality available
in AspectJ. However, Spring.NET AOP provides an excellent solution to
most problems in .NET applications that are amenable to AOP.</remark>
<remark>Thus, it is common to see Spring.NET's AOP functionality used in
conjunction with a Spring.NET IoC container. AOP advice is specified
using normal object definition syntax (although this allows powerful
"autoproxying" capabilities); advice and pointcuts are themselves
managed by Spring.NET IoC.</remark>
</sect2>
<sect2 xml:id="aop-introduction-proxies">
<title>AOP Proxies in Spring.NET</title>
<para>Spring.NET generates AOP proxies at runtime using classes from the
System.Reflection.Emit namespace to create necessary IL code for the
proxy class. This results in proxies that are very efficient and do not
impose any restrictions on the inheritance hierarchy.</para>
<para>Another common approach to AOP proxy implementation in .NET is to
use ContextBoundObject and the .NET remoting infrastructure as an
interception mechanism. We are not very fond of ContextBoundObject
approach because it requires classes that need to be proxied to inherit
from the ContextBoundObject either directly or indirectly. In our
opinion this an unnecessary restriction that influences how you should
design your object model and also excludes applying AOP to "3rd party"
classes that are not under your direct control. Context-bound proxies
are also an order of magnitude slower than IL-generated proxies, due to
the overhead of the context switching and .NET remoting
infrastructure.</para>
<para>Spring.NET AOP proxies are also "smart" - in that because proxy
configuration is known during proxy generation, the generated proxy can
be optimized to invoke target methods via reflection only when necessary
(i.e. when there are advices applied to the target method). In all other
cases the target method will be called directly, thus avoiding
performance hit caused by the reflective invocation.</para>
<para>Finally, Spring.NET AOP proxies will never return a raw reference
to a target object. Whenever a target method returns a raw reference to
a target object (i.e. "return this;"), AOP proxy will recognize what
happened and will replace the return value with a reference to itself
instead.</para>
<para>The current implementation of the AOP proxy generator uses object
composition to delegate calls from the proxy to a target object, similar
to how you would implement a classic Decorator pattern. This means that
classes that need to be proxied have to implement one or more
interfaces, which is in our opinion not only a less-intruding
requirement than ContextBoundObject inheritance requirements, but also a
good practice that should be followed anyway for the service classes
that are most common targets for AOP proxies.</para>
<para>In a future release we will implement proxies using inheritance,
which will allow you to proxy classes without interfaces as well and
will remove some of the remaining raw reference issues that cannot be
solved using composition-based proxies.</para>
</sect2>
</sect1>
<sect1 xml:id="aop-pointcuts">
<title>Pointcut API in Spring.NET</title>
<para>Let's look at how Spring.NET handles the crucial pointcut
concept.</para>
<sect2 xml:id="aop-pointcut-concepts">
<title>Concepts</title>
<para>Spring.NET'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>Spring.Aop.IPointcut</literal> interface is the
central interface, used to target advices to particular types and
methods. The complete interface is shown below:</para>
<programlisting language="csharp">public interface IPointcut
{
ITypeFilter TypeFilter { get; }
IMethodMatcher MethodMatcher { get; }
}</programlisting>
<para>Splitting the <literal>IPointcut</literal> interface into two
parts allows reuse of type and method matching parts, and fine-grained
composition operations (such as performing a "union" with another method
matcher).</para>
<para>The <literal>ITypeFilter</literal> 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
types will be matched:</para>
<programlisting language="csharp">public interface ITypeFilter
{
bool Matches(Type type);
}</programlisting>
<para>The <literal>IMethodMatcher</literal> interface is normally more
important. The complete interface is shown below:</para>
<programlisting language="csharp">public interface IMethodMatcher
{
bool IsRuntime { get; }
bool Matches(MethodInfo method, Type targetType);
bool Matches(MethodInfo method, Type targetType, object[] args);
}</programlisting>
<para>The <literal>Matches(MethodInfo, Type) </literal>method is used to
test whether this pointcut will ever match a given method on a target
type. 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> property for the
<literal>IMethodMatcher</literal> 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 <literal>IMethodMatchers</literal> are static, meaning that
their <literal>IsRuntime</literal> property returns false. In this case,
the 3-argument <literal>Matches</literal> method will never be
invoked.</para>
<remark>Whenever possible, try to make pointcuts static... this allows
the AOP framework to cache the results of pointcut evaluation when an
AOP proxy is created.</remark>
</sect2>
<sect2 xml:id="aop-pointcut-operations">
<title>Operations on pointcuts</title>
<para>Spring.NET supports operations on pointcuts: notably,
<emphasis>union</emphasis> and <emphasis>intersection</emphasis>.</para>
<para>Union means the methods that either pointcut matches.</para>
<para>Intersection means the methods that both pointcuts match.</para>
<para>Union is usually more useful.</para>
<para>Pointcuts can be composed using the static methods in the
<emphasis>Spring.Aop.Support.Pointcuts</emphasis> class, or using the
<emphasis>ComposablePointcut</emphasis> class in the same
namespace.</para>
</sect2>
<sect2 xml:id="aop-convenience-impls">
<title>Convenience pointcut implementations</title>
<para>Spring.NET provides several convenient pointcut implementations.
Some can be used out of the box; others are intended to be subclassed in
application-specific pointcuts.</para>
<sect3 xml:id="aop-static-pointcuts">
<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--and best--for most usages. It's possible for Spring.NET 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.NET.</para>
<sect4 xml:id="aop-regularexpression-pointcut">
<title>Regular expression pointcuts</title>
<para>One obvious way to specify static pointcuts is using regular
expressions. Several AOP frameworks besides Spring.NET make this
possible. The
<literal>Spring.Aop.Support.SdkRegularExpressionMethodPointcut</literal>
class is a generic regular expression pointcut, that uses the
regular expression classes from the .NET BCL.</para>
<para>Using this 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.). The
matching is done against the full class name so you can use this
pointcut if you would like to apply advice to all the classes in a
particular namespace.</para>
<para>The usage is shown below:</para>
<programlisting language="myxml">&lt;object id="settersAndAbsquatulatePointcut"
type="Spring.Aop.Support.SdkRegularExpressionMethodPointcut, Spring.Aop"&gt;
&lt;property name="patterns"&gt;
&lt;list&gt;
&lt;value&gt;.*set.*&lt;/value&gt;
&lt;value&gt;.*absquatulate&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;/object&gt;</programlisting>
<para>As a convenience, Spring provides the
<literal>RegularExpressionMethodPointcutAdvisor</literal> class that
allows us to reference an <literal>IAdvice</literal> instance as
well as defining the pointcut rules (remember that an
<literal>IAdvice</literal> instance can be an interceptor, before
advice, throws advice etc.) This simplifies wiring, as the one
object serves as both pointcut and advisor, as shown below:</para>
<programlisting language="myxml">&lt;object id="settersAndAbsquatulateAdvisor"
type="Spring.Aop.Support.RegularExpressionMethodPointcutAdvisor, Spring.Aop"&gt;
&lt;property name="advice"&gt;
&lt;ref local="objectNameOfAopAllianceInterceptor"/&gt;
&lt;/property&gt;
&lt;property name="patterns"&gt;
&lt;list&gt;
&lt;value&gt;.*set.*&lt;/value&gt;
&lt;value&gt;.*absquatulate&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;/object&gt;</programlisting>
<para>The <literal>RegularExpressionMethodPointcutAdvisor</literal>
class can be used with any <literal>Advice</literal> type.</para>
If you only have one pattern you can use the property name
<literal>pattern</literal>
and specify a single value instead of using the property name
<literal>patterns</literal>
and specifying a list.
<para>You may also specify a <literal>Regex</literal> object from
the <literal>System.Text.RegularExpressions</literal> namespace. The
built in <literal>RegexConverter</literal> class will perform the
conversion. See <xref linkend="object-objects-builtin-converters" />
for more information on Spring's build in type converters. The Regex
object is created as any other object within the IoC container.
Using an inner-object definition for the Regex object is a handy way
to keep the definition close to the PointcutAdvisor declaration.
Note that the class
<literal>SdkRegularExpressionMethodPointcut</literal> has a
<methodname>DefaultOptions</methodname> property to set the regular
expression options if they are not explicitly specified in the
constructor.</para>
</sect4>
<sect4 xml:id="aop-attribute-pointcut">
<title>Attribute pointcuts</title>
<para>Pointcuts can be specified by matching an attribute type that
is associated with a method. Advice associated with this pointcut
can then read the metadata associated with the attribute to
configure itself. The class
<literal>AttributeMatchMethodPointcut</literal> provides this
functionality. Sample usage that will match all methods that have
the attribute <literal>Spring.Attributes.CacheAttribute</literal> is
shown below. <programlisting language="myxml">&lt;object id="cachePointcut" type="Spring.Aop.Support.AttributeMatchMethodPointcut, Spring.Aop"&gt;
&lt;property name="Attribute" value="Spring.Attributes.CacheAttribute, Spring.Core"/&gt;
&lt;/object&gt;</programlisting></para>
<para>This can be used with a
<literal>DefaultPointcutAdvisor</literal> as shown
below<programlisting language="myxml">&lt;object id="cacheAspect" type="Spring.Aop.Support.DefaultPointcutAdvisor, Spring.Aop"&gt;
&lt;property name="Pointcut"&gt;
&lt;object type="Spring.Aop.Support.AttributeMatchMethodPointcut, Spring.Aop"&gt;
&lt;property name="Attribute" value="Spring.Attributes.CacheAttribute, Spring.Core"/&gt;
&lt;/object&gt;
&lt;/property&gt;
&lt;property name="Advice" ref="aspNetCacheAdvice"/&gt;
&lt;/object&gt; </programlisting> where aspNetCacheAdvice is an implementation
of an <literal>IMethodInterceptor</literal> that caches method
return values. See the SDK docs for
<literal>Spring.Aop.Advice.CacheAdvice</literal> for more
information on this particular advice.</para>
<para>As a convenience the class
<literal>AttributeMatchMethodPointcutAdvisor</literal> is provided
to defining an attribute based Advisor as a somewhat shorter
alternative to using the generic DefaultPointcutAdvisor. An example
is shown below.<programlisting language="myxml">&lt;object id="AspNetCacheAdvice" type="Spring.Aop.Support.AttributeMatchMethodPointcutAdvisor, Spring.Aop"&gt;
&lt;property name="advice"&gt;
&lt;object type="Aspect.AspNetCacheAdvice, Aspect"/&gt;
&lt;/property&gt;
&lt;property name="attribute" value="Framework.AspNetCacheAttribute, Framework" /&gt;
&lt;/object&gt;</programlisting></para>
</sect4>
</sect3>
<sect3 xml:id="aop-dynamic-pointucts">
<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>
<sect4>
<title>Control Flow Pointcuts</title>
<para>Spring.NET 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 another pointcut.). A control flow pointcut is
dynamic because it is evaluated against the current call stack for
each method invocation. For example, if method ClassA.A() calls
ClassB.B() then the execution of ClassB.B() has occurred in
ClassA.A()'s control flow. A control flow pointcut allows advice to
be applied to the method ClassA.A() but only when called from
ClassB.B() and not when ClassA.A() is executed from another call
stack. Control flow pointcuts are specified using the
<literal>Spring.Aop.Support.ControlFlowPointcut
</literal>class.<note>
<para>Control flow pointcuts are significantly more expensive to
evaluate at runtime than even other dynamic pointcuts.</para>
</note></para>
<para>When using control flow point cuts some attention should be
paid to the fact that at runtime the JIT compiler can inline the
methods, typically for increased performance, but with the
consequence that the method no longer appears in the current call
stack. This is because inlining takes the callee's IL code and
inserts it into the caller's IL code effectively removing the method
call. The information returned from
<literal>System.Diagnostics.StackTrace</literal>, used in the
implementation of <literal>ControlFlowPointcut</literal> is subject
to these optimizations and therefore a control flow pointcut will
not match if the method has been inlined.</para>
<para>Generally speaking, a method will be a candidate for inlining
when its code is 'small', just a few lines of code (less than 32
bytes of IL). For some interesting reading on this process read
David Notario's blog entries (<ulink
url="http://blogs.msdn.com/davidnotario/archive/2004/10/28/248953.aspx">JIT
Optimizations I</ulink> and <ulink
url="http://blogs.msdn.com/davidnotario/archive/2004/11/01/250398.aspx">JIT
Optimizations II</ulink>). Additionally, when an assembly is
compiled with a Release configuration the assembly metadata
instructs the CLR to enable JIT optimizations. When compiled with a
Debug configuration the CLR will disable (some?) these
optimizations. Empirically, method inlining is turned off in a Debug
configuration.</para>
<para>The way to ensure that your control flow pointcut will not be
overlooked because of method inlining is to apply the
<literal>System.Runtime.CompilerServices.MethodImplAttribute</literal>
attribute with the value
<literal>MethodImplOptions.NoInlining</literal>. In this (somewhat
artificial) simple example, if the code is compiled in release mode
it will not match a control flow pointcut for the method
"GetAge".</para>
<programlisting language="csharp">public int GetAge(IPerson person)
{
return person.GetAge();
}</programlisting>
<para>However, applying the attributes as shown below will prevent
the method from being inlined even in a release build.</para>
<programlisting language="csharp">[MethodImpl(MethodImplOptions.NoInlining)]
public int GetAge(IPerson person)
{
return person.GetAge();
}</programlisting>
</sect4>
</sect3>
</sect2>
<sect2>
<title>Custom pointcuts</title>
<para>Because pointcuts in Spring.NET are .NET types, rather than
language features (as in AspectJ) it is possible to declare custom
pointcuts, whether static or dynamic. However, there is no support out
of the box for the sophisticated pointcut expressions that can be coded
in the AspectJ syntax. However, custom pointcuts in Spring.NET can be as
arbitrarily complex as any object model.</para>
<para>Spring.NET provides useful pointcut superclasses to help you to
implement your own pointcuts.</para>
<para>Because static pointcuts are the most common and generally useful
pointcut type, you'll probably subclass
<literal>StaticMethodMatcherPointcut</literal>, as shown below. This
requires you to implement just one abstract method (although it is
possible to override other methods to customize behaviour):</para>
<programlisting language="csharp">public class TestStaticPointcut : StaticMethodMatcherPointcut {
public override bool Matches(MethodInfo method, Type targetType) {
// return true if custom criteria match
}
}</programlisting>
</sect2>
</sect1>
<sect1 xml:id="aop-advice-types">
<title>Advice API in Spring.NET</title>
<para>Let's now look at how Spring.NET AOP handles advice.</para>
<sect2 xml:id="aop-introduction-advice-lifecycle">
<title>Advice Lifecycle</title>
<para>Spring.NET advices 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>
</sect2>
<sect2 xml:id="aop-introduction-advice-types">
<title>Advice types</title>
<para>Spring.NET 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>
<sect3>
<title>Interception Around Advice</title>
<para>The most fundamental advice type in Spring.NET is
<emphasis>interception around advice</emphasis>.</para>
<para>Spring.NET is compliant with the AOP Alliance interface for
around advice using method interception. Around advice is implemented
using the following interface:</para>
<programlisting language="csharp">public interface IMethodInterceptor : IInterceptor
{
object Invoke(IMethodInvocation invocation);
}</programlisting>
<para>The <literal>IMethodInvocation</literal> argument to the
<literal>Invoke()</literal> method exposes the method being invoked;
the target joinpoint; the AOP proxy; and the arguments to the method.
The <literal>Invoke()</literal> method should return the invocation's
result: the return value of the joinpoint.</para>
<para>A simple <literal>IMethodInterceptor</literal> implementation
looks as follows:</para>
<programlisting language="csharp">public class DebugInterceptor : IMethodInterceptor {
public object Invoke(IMethodInvocation invocation) {
Console.WriteLine("Before: invocation=[{0}]", invocation);
object rval = invocation.Proceed();
Console.WriteLine("Invocation returned");
return rval;
}
}</programlisting>
<para>Note the call to the IMethodInvocation's
<literal>Proceed()</literal> method. This proceeds down the
interceptor chain towards the joinpoint. Most interceptors will invoke
this method, and return its return value. However, an
IMethodInterceptor, like any around advice, can return a different
value or throw an exception rather than invoke the
<literal>Proceed()</literal> method. However, you don't want to do
this without good reason!</para>
</sect3>
<sect3>
<title>Before advice</title>
<para>A simpler advice type is a <emphasis role="bold">before
advice</emphasis>. This does not need an
<literal>IMethodInvocation</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>IMethodBeforeAdvice</literal> interface is shown
below.</para>
<programlisting language="csharp">public interface IMethodBeforeAdvice : IBeforeAdvice
{
void Before(MethodInfo method, object[] args, object target);
}</programlisting>
<para>Note the return type is <literal>void</literal>. Before advice
can insert custom behaviour before the joinpoint 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.NET, which counts all
methods that return normally:</para>
<programlisting language="csharp">public class CountingBeforeAdvice : IMethodBeforeAdvice {
private int count;
public void Before(MethodInfo method, object[] args, object target) {
++count;
}
public int Count {
get { return count; }
}
}</programlisting>
<remark>Before advice can be used with any pointcut.</remark>
</sect3>
<sect3 xml:id="aop-introduction-advice-types-throws">
<title>Throws advice</title>
<para>Throws advice is invoked after the return of the joinpoint if
the joinpoint threw an exception. The
<literal>Spring.Aop.IThrowsAdvice</literal> interface does not contain
any methods: it is a tag interface identifying that the implementing
advice object implements one or more typed throws advice methods.
These throws advice methods must be of the form:</para>
<programlisting language="csharp">AfterThrowing([MethodInfo method, Object[] args, Object target], Exception subclass)</programlisting>
<para>Throws-advice methods must be named
<literal>'AfterThrowing'</literal>. The return value will be ignored
by the Spring.NET AOP framework, so it is typically
<literal>void</literal>. With regard to the method arguments, only the
last argument is required. Thus there are <emphasis>exactly</emphasis>
one <emphasis>or</emphasis> four arguments, depending on whether the
advice method is interested in the method, method arguments and the
target object.</para>
<para>The following method snippets show examples of throws
advice.</para>
<para>This advice will be invoked if a
<literal>RemotingException</literal> is thrown (including
subclasses):</para>
<programlisting language="csharp">public class RemoteThrowsAdvice : IThrowsAdvice {
public void AfterThrowing(RemotingException ex) {
// Do something with remoting exception
}
}</programlisting>
<para>The following advice is invoked if a
<literal>SqlException</literal> 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="csharp">public class SqlExceptionThrowsAdviceWithArguments : IThrowsAdvice {
public void AfterThrowing(MethodInfo method, object[] args, object target, SqlException ex) {
// Do something will all arguments
}
}</programlisting>
<para>The final example illustrates how these two methods could be
used in a single class, which handles both
<literal>RemotingException</literal> and
<literal>SqlException</literal>. Any number of throws advice methods
can be combined in a single class, as can be seen in the following
example.</para>
<programlisting language="csharp">public class CombinedThrowsAdvice : IThrowsAdvice {
public void AfterThrowing(RemotingException ex) {
// Do something with remoting exception
}
public void AfterThrowing(MethodInfo method, object[] args, object target, SqlException ex) {
// Do something will all arguments
}
}</programlisting>
<para>Finally, it is worth stating that throws advice is only applied
to the actual exception being thrown. What does this mean? Well, it
means that if you have defined some throws advice that handles
<literal>RemotingException</literal>s, the applicable
<literal>AfterThrowing</literal> method will <emphasis
role="bold">only</emphasis> be invoked if the type of the thrown
exception is <literal>RemotingException</literal>... if a
<literal>RemotingException</literal> has been thrown and subsequently
wrapped inside another exception before the exception bubbles up to
the throws advice interceptor, then the throws advice that handles
<literal>RemotingException</literal>s will <emphasis
role="bold">never</emphasis> be called. Consider a business method
that is advised by throws advice that handles
<literal>RemotingException</literal>s; if during the course of a
method invocation said business method throws a RemoteException... and
subsequently wraps said <literal>RemotingException</literal> inside a
business-specific <literal>BadConnectionException</literal> (see the
code snippet below) before throwing the exception, then the throws
advice will never be able to respond to the
<literal>RemotingException</literal>... because all the throws advice
sees is a <literal>BadConnectionException</literal>. The fact that the
<literal>RemotingException</literal> is wrapped up inside the
<literal>BadConnectionException</literal> is immaterial.</para>
<programlisting language="csharp">public void BusinessMethod()
{
try
{
// do some business operation...
}
catch (RemotingException ex)
{
throw new BadConnectionException("Couldn't connect.", ex);
}
}</programlisting>
<note>
Please note that throws advice can be used with any pointcut.
</note>
</sect3>
<sect3>
<title>After Returning advice</title>
<para>An after returning advice in Spring.NET must implement the
<literal>Spring.Aop.IAfterReturningAdvice</literal> interface, shown
below:</para>
<programlisting language="csharp">public interface IAfterReturningAdvice : IAdvice
{
void AfterReturning(object returnValue, MethodBase method, object[] args, object target);
}</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="csharp">public class CountingAfterReturningAdvice : IAfterReturningAdvice {
private int count;
public void AfterReturning(object returnValue, MethodBase m, object[] args, object target) {
++count;
}
public int Count {
get { 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>
<note>
Please note that after-returning advice can be used with any pointcut.
</note>
</sect3>
<sect3 xml:id="aop-advice-ordering">
<title>Advice Ordering</title>
<para>When multiple pieces of advice want to run on the same joinpoint
the precedence is determined by having the advice implement the
IOrdered interface or by specifying order information on an
advisor.</para>
</sect3>
<sect3>
<title>Introduction advice</title>
<para>Spring.NET allows you to add new methods and properties to an
advised class. This would typically be done when the functionality you
wish to add is a crosscutting concern and want to introduce this
functionality as a change to the static structure of the class
hierarchy. For example, you may want to cast objects to the
introduction interface in your code. Introductions are also a means to
emulate multiple inheritance.</para>
<para>Introduction advice is defined by using a normal interface
declaration that implements the tag interface
<literal>IAdvice</literal>. <note>The need for implementing this
marker interface will likely be removed in future versions.</note> As
an example, consider the interface <literal>IAuditable</literal> that
describes the last modified time of an object.</para>
<programlisting language="csharp">public interface IAuditable : IAdvice
{
DateTime LastModifiedDate
{
get;
set;
}
}</programlisting>
where
<programlisting language="csharp">public interface IAdvice
{
}</programlisting>
<para>Access to the advised object can be obtained by implementing the
interface <literal>ITargetAware</literal> <programlisting
language="csharp">public interface ITargetAware
{
IAopProxy TargetProxy
{
set;
}
}</programlisting> with the <literal>IAopProxy</literal> reference providing a
layer of indirection through which the advised object can be accessed.
<programlisting language="csharp">public interface IAopProxy
{
object GetProxy();
}</programlisting></para>
<para>A simple class that demonstrates this functionality is shown
below. <programlisting language="csharp">public interface IAuditable : IAdvice, ITargetAware
{
DateTime LastModifiedDate
{
get;
set;
}
}</programlisting></para>
<para>A class that implements this interface is shown below.</para>
<programlisting language="csharp">public class AuditableMixin : IAuditable
{
private DateTime date;
private IAopProxy targetProxy;
public AuditableMixin()
{
date = new DateTime();
}
public DateTime LastModifiedDate
{
get { return date; }
set { date = value; }
}
public IAopProxy TargetProxy
{
set { targetProxy = value; }
}
}</programlisting>
<para>Introduction advice is not associated with a pointcut, since it
applies at the class and not the method level. As such, introductions
use their own subclass of the interface <literal>IAdvisor</literal>,
namely <literal>IIntroductionAdvisor</literal>, to specify the types
that the introduction can be applied to.</para>
<programlisting language="csharp">public interface IIntroductionAdvisor : IAdvisor
{
ITypeFilter TypeFilter { get; }
Type[] Interfaces { get; }
void ValidateInterfaces();
}</programlisting>
<para>The <literal>TypeFilter</literal> property returns the filter
that determines which target classes this introduction should apply
to.</para>
<para>The <literal>Interfaces</literal> property returns the
interfaces introduced by this advisor.</para>
<para>The <literal>ValidateInterfaces()</literal> method is used
internally to see if the introduced interfaces can be implemented by
the introduction advice.</para>
<para>Spring.NET provides a default implementation of this interface
(the <literal>DefaultIntroductionAdvisor</literal> class) that should
be sufficient for the majority of situations when you need to use
introductions. The most simple implementation of an introduction
advisor is a subclass that simply passes a new instance the base
constructor. Passing a new instance is important since we want a new
instance of the mixin classed used for each advised object.</para>
<programlisting language="csharp">public class AuditableAdvisor : DefaultIntroductionAdvisor
{
public AuditableAdvisor() : base(new AuditableMixin())
{
}
}</programlisting>
<para>Other constructors let you explicitly specify the interfaces of
the class that will be introduced. See the SDK documentation for more
details.</para>
<para>We can apply this advisor Programatically, using the
<literal>IAdvised.AddIntroduction(),</literal> method, or (the
recommended way) in XML configuration using the
<literal>IntroductionNames</literal> property on
<literal>ProxyFactoryObject</literal>, which will be discussed
later.</para>
<remark>Unlike the AOP implementation in the Spring Framework for
Java, introduction advice in Spring.NET is not implemented as a
specialized type of interception advice. The advantage of this
approach is that introductions are not kept in the interceptor chain,
which allows some significant performance optimizations. When a method
is called that has no interceptors, a direct call is used instead of
reflection regardless of whether the target method is on the target
object itself or one of the introductions. This means that introduced
methods perform the same as target object methods, which could be
useful for adding introductions to fine grained objects. The
disadvantage is that if the mixin functionality would benefit from
having access to the calling stack, it is not available. Introductions
with this functionality will be addressed in a future version of
Spring.NET AOP.</remark>
</sect3>
</sect2>
</sect1>
<sect1 xml:id="aop-advisors">
<title>Advisor API in Spring.NET</title>
<para>In Spring.NET, an advisor is a modularization of an aspect. Advisors
typically incorporate both an advice and a pointcut.</para>
<para>Apart from the special case of introductions, any advisor can be
used with any advice. The
<literal>Spring.Aop.Support.DefaultPointcutAdvisor</literal> class is the
most commonly used advisor implementation. For example, it can be used
with a <literal>IMethodInterceptor</literal>,
<literal>IBeforeAdvice</literal> or <literal>IThrowsAdvice</literal> and
any pointcut definition.</para>
<para>Other convenience implementations provided are:
<literal>AttributeMatchMethodPointcutAdvisor</literal> shown in usage
previously in <xref linkend="aop-attribute-pointcut" /> for use with
attribute based pointcuts.
<literal>RegularExpressionMethodPointcutAdvisor</literal> that will apply
pointcuts based on the matching a regular expression to method
names.</para>
<para>It is possible to mix advisor and advice types in Spring.NET in the
same AOP proxy. For example, you could use a interception around advice,
throws advice and before advice in one proxy configuration: Spring.NET
will automatically create the necessary interceptor chain.</para>
</sect1>
<sect1 xml:id="aop-proxyfactoryobject">
<title>Using the ProxyFactoryObject to create AOP proxies</title>
<para>If you're using the Spring.NET IoC container for your business
objects - generally a good idea - you will want to use one of Spring.NET's
AOP-specific <literal>IFactoryObject</literal> implementations (remember
that a factory object introduces a layer of indirection, enabling it to
create objects of a different type - <xref
linkend="objects-advancedproperty-setting" />).</para>
<para>The basic way to create an AOP proxy in Spring.NET is to use the
<literal>Spring.Aop.Framework.ProxyFactoryObject</literal> class. This
gives complete control over ordering and application of the pointcuts and
advice that will apply to your business objects. However, there are
simpler options that are preferable if you don't need such control.</para>
<sect2 xml:id="aop-pfb-1">
<title>Basics</title>
<para>The <literal>ProxyFactoryObject</literal>, like other Spring.NET
<literal>IFactoryObject</literal> implementations, introduces a level of
indirection. If you define a <literal>ProxyFactoryObject</literal> with
name <literal>foo</literal>, what objects referencing
<literal>foo</literal> see is not the
<literal>ProxyFactoryObject</literal> instance itself, but an object
created by the <literal>ProxyFactoryObject's</literal> 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>ProxyFactoryObject</literal> or other IoC-aware classes that
create AOP proxies, is that it means that advice 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>
</sect2>
<sect2 xml:id="aop-pfb-2">
<title>ProxyFactoryObject Properties</title>
<para>Like most <literal>IFactoryObject</literal> implementations
provided with Spring.NET, the <literal>ProxyFactoryObject</literal> is
itself a Spring.NET configurable object. Its properties are used
to:</para>
<itemizedlist>
<listitem>
<para>Specify the target object that is to be proxied.</para>
</listitem>
<listitem>
<para>Specify the advice that is to be applied to the proxy.</para>
</listitem>
</itemizedlist>
<para>Some key properties are inherited from the
<literal>Spring.Aop.Framework.ProxyConfig</literal> class: this class is
the superclass for all AOP proxy factories in Spring.NET. Some of the
key properties include:</para>
<itemizedlist>
<listitem>
<para><literal>ProxyTargetType</literal>: a boolean value that
should be set to true if the target class is to be proxied directly,
as opposed to just proxying the interfaces exposed on the target
class.</para>
</listitem>
<listitem>
<para><literal>Optimize</literal>: whether to apply aggressive
optimization to created proxies. Don't use this setting unless you
understand how the relevant AOP proxy handles optimization. The
exact meaning of this flag will differ between proxy implementations
and will generally result in a trade off between proxy creation time
and runtime performance. Optimizations may be ignored by certain
proxy implementations and may be disabled silently based on the
value of other properties such as
<literal>ExposeProxy</literal>.</para>
</listitem>
<listitem>
<para><literal>IsFrozen</literal>: whether advice changes should be
disallowed once the proxy factory has been configured. The default
is false.</para>
</listitem>
<listitem>
<para><literal>ExposeProxy</literal>: whether the current proxy
should be exposed via the <literal>AopContext</literal> so that it
can be accessed by the target. (It's available via the
<literal>IMethodInvocation</literal> without the need for the
<literal>AopContext</literal>.) If a target needs to obtain the
proxy and <literal>ExposeProxy</literal> is <literal>true</literal>,
the target can use the <literal>AopContext.CurrentProxy</literal>
property.</para>
</listitem>
<listitem>
<para><literal>AopProxyFactory</literal>: the implementation of
<literal>IAopProxyFactory</literal> to use when generating a proxy.
Offers a way of customizing whether to use remoting proxies, IL
generation or any other proxy strategy. The default implementation
will use IL generation to create composition-based proxies.</para>
</listitem>
</itemizedlist>
<para>Other properties specific to the
<literal>ProxyFactoryObject</literal> class include:</para>
<itemizedlist>
<listitem>
<para><literal>ProxyInterfaces</literal>: the array of
<literal>string</literal> interface names we're proxying.</para>
</listitem>
<listitem>
<para><literal>InterceptorNames</literal>: <literal>string</literal>
array of <literal>IAdvisor</literal>, interceptor or other advice
names to apply. Ordering is significant... first come, first served
that is. The first interceptor in the list will be the first to be
able to interceptor the invocation (assuming it concerns a regular
MethodInterceptor or BeforeAdvice).</para>
<para>The names are object names in the current container, including
objectnames from container hierarchies. You can't mention object
references here since doing so would result in the
<literal>ProxyFactoryObject</literal> ignoring the singleton setting
of the advise.</para>
</listitem>
<listitem>
<para><literal>IntroductionNames</literal>: The names of objects in
the container that will be used as introductions to the target
object. If the object referred to by name does not implement the
<literal>IIntroductionAdvisor</literal> it will be passed to the
default constructor of <literal>DefaultIntroductionAdvisor</literal>
and all of the objects interfaces will be added to the target
object. Objects that implement the
<literal>IIntroductionAdvisor</literal> interface will be used as
is, giving you a finer level of control over what interfaces you may
want to expose and the types for which they will be matched
against.</para>
</listitem>
<listitem>
<para><literal>IsSingleton</literal>: whether or not the factory
should return a single proxy object, no matter how often the
<literal>GetObject()</literal> method is called. Several
<literal>IFactoryObject</literal> implementations offer such a
method. The default value is <literal>true</literal>. If you would
like to be able to apply advice on a per-proxy object basis, use a
<literal>IsSingleton</literal> value of <literal>false</literal> and
a <literal>IsFrozen</literal> value of <literal>false</literal>. If
you want to use stateful advice--for example, for stateful
mixins--use prototype advices along with a
<literal>IsSingleton</literal> value of
<literal>false</literal>.</para>
</listitem>
</itemizedlist>
</sect2>
<sect2 xml:id="aop-proxying-interfaces">
<title>Proxying Interfaces</title>
<para>Let's look at a simple example of
<literal>ProxyFactoryObject</literal> in action. This example involves:
<itemizedlist>
<listitem>
<para>A target object that will be proxied. This is the
"personTarget" object definition in the example below.</para>
</listitem>
<listitem>
<para>An <literal>IAdvisor</literal> and an
<literal>IInterceptor</literal> used to provide advice.</para>
</listitem>
<listitem>
<para>An AOP proxy object definition specifying the target object
(the personTarget object) and the interfaces to proxy, along with
the advices to apply.</para>
</listitem>
</itemizedlist></para>
<programlisting language="myxml">&lt;object id="personTarget" type="MyCompany.MyApp.Person, MyCompany"&gt;
&lt;property name="name" value="Tony"/&gt;
&lt;property name="age" value="51"/&gt;
&lt;/object&gt;
&lt;object id="myCustomInterceptor" type="MyCompany.MyApp.MyCustomInterceptor, MyCompany"&gt;
&lt;property name="customProperty" value="configuration string"/&gt;
&lt;/object&gt;
&lt;object id="debugInterceptor" type="Spring.Aop.Advice.DebugAdvice, Spring.Aop"&gt;
&lt;/object&gt;
&lt;object id="person" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop"&gt;
&lt;property name="proxyInterfaces" value="MyCompany.MyApp.IPerson"/&gt;
&lt;property name="target" ref="personTarget"/&gt;
&lt;property name="interceptorNames"&gt;
&lt;list&gt;
&lt;value&gt;debugInterceptor&lt;/value&gt;
&lt;value&gt;myCustomInterceptor&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;/object&gt;</programlisting>
<para>Note that the <literal>InterceptorNames</literal> property takes a
list of <literal>strings</literal>: the object names of the interceptor
or advisors in the current context. Advisors, interceptors, before,
after returning and throws advice objects can be used. The ordering of
advisors is significant.</para>
<remark>You might be wondering why the list doesn't hold object
references. The reason for this is that if the
<literal>ProxyFactoryObject's</literal> 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 context; holding a reference isn't
sufficient.</remark>
<para>The "person" object definition above can be used in place of an
<literal>IPerson</literal> implementation, as follows:</para>
<programlisting language="csharp">IPerson person = (IPerson) factory.GetObject("person");</programlisting>
<para>Other objects in the same IoC context can express a strongly typed
dependency on it, as with an ordinary .NET object:</para>
<para>
<programlisting language="myxml">&lt;object id="personUser" type="MyCompany.MyApp.PersonUser, MyCompany"&gt;
&lt;property name="person" ref="person"/&gt;
&lt;/object&gt;</programlisting>
</para>
<para>The <literal>PersonUser</literal> class in this example would
expose a property of type <literal>IPerson</literal>. As far as it's
concerned, the AOP proxy can be used transparently in place of a "real"
person implementation. However, its type would be a proxy type. It would
be possible to cast it to the <literal>IAdvised</literal> interface
(discussed below).</para>
<para>It's possible to conceal the distinction between target and proxy
using an anonymous <emphasis>inline object</emphasis>, as follows. (for
more information on inline objects see <xref
linkend="objects-inline-object" />.) Only the
<literal>ProxyFactoryObject</literal> definition is different; the
advice is included only for completeness:</para>
<programlisting language="myxml">&lt;object id="myCustomInterceptor" type="MyCompany.MyApp.MyCustomInterceptor, MyCompany"&gt;
&lt;property name="customProperty" value="configuration string"/&gt;
&lt;/object&gt;
&lt;object id="debugInterceptor" type="Spring.Aop.Advice.DebugAdvice, Spring.Aop"&gt;
&lt;/object&gt;
&lt;object id="person" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop"&gt;
&lt;property name="proxyInterfaces" value="MyCompany.MyApp.IPerson"/&gt;
&lt;property name="target"&gt;
&lt;!-- Instead of using a reference to target, just use an inline object --&gt;
&lt;object type="MyCompany.MyApp.Person, MyCompany"&gt;
&lt;property name="name" value="Tony"/&gt;
&lt;property name="age" value="51"/&gt;
&lt;/object&gt;
&lt;/property&gt;
&lt;property name="interceptorNames"&gt;
&lt;list&gt;
&lt;value&gt;debugInterceptor&lt;/value&gt;
&lt;value&gt;myCustomInterceptor&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;/object&gt;</programlisting>
<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 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 ProxyFactoryObject 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>
</sect2>
<sect3>
<title>Applying advice on a per-proxy basis.</title>
<para>Let's look at an example of configuring the proxy objects
retrieved from <literal>ProxyFactoryObject</literal>.</para>
<para><programlisting language="myxml">
&lt;!-- create the object to reference --&gt;
&lt;object id="RealObjectTarget" type="MyRealObject" singleton="false"/&gt;
&lt;!-- create the proxied object for everyone to use--&gt;
&lt;object id="MyObject" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop"&gt;
&lt;property name="proxyInterfaces" value="MyInterface" /&gt;
&lt;property name="isSingleton" value="false"/&gt;
&lt;property name="targetName" value="RealObjectTarget" /&gt;
&lt;/object&gt;
</programlisting>If you are using a prototype as the target you must
set the <literal>TargetName</literal> property with the name/object id
of your object and not use the property <literal>Target</literal> with a
reference to that object. This will then allow a new proxy to be created
around a new prototype target instance.</para>
<para>Consider the above Spring.Net object configuration. Notice that
the <literal>IsSingleton</literal> property of the
<literal>ProxyFactoryObject</literal> instance is set to false. This
means that each proxy object will be unique. Thus, you can configure
each proxy object with its' own individual advice(s) using the following
syntax <programlisting language="csharp">
// Will return un-advised instance of proxy object
MyInterface myProxyObject1 = (MyInterface)ctx.GetObject("MyObject");
// myProxyObject1 instance now has an advice attached to it.
IAdvised advised = (IAdvised)myProxyObject1;
advised.AddAdvice( new DebugAdvice() );
// Will return a new, un-advised instance of proxy object
MyInterface myProxyObject2 = (MyInterface)ctx.GetObject("MyObject");
</programlisting></para>
</sect3>
<sect2>
<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>IPerson</literal> interface, rather we needed to advise a class
called <literal>Person</literal> that didn't implement any business
interface. In this case the <literal>ProxyFactoryObject</literal> will
proxy all public virtual methods and properties if no interfaces are
explicitly specified or if no interfaces are found to be present on the
target object. One can configure Spring.NET to force the use of class
proxies, rather than interface proxies, by setting the
<literal>ProxyTargetType</literal> property on the
<literal>ProxyFactoryObject</literal> above to true.</para>
<para>Class proxying works by generating a subclass of the target class
at runtime. Spring.NET 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>Class proxying should generally be transparent to users. However,
there is an important issue to consider:
<emphasis>Non-<literal>virtual</literal> methods can't be advised, as
they can't be overridden.</emphasis> This may be a limiting factor when
using existing code as it has been common practice not to declare
methods as virtual by default.</para>
</sect2>
<sect2>
<title>Concise proxy definitions</title>
<para>Especially when defining transactional proxies, if you do not make
use of the transaction namespace, you may end up with many similar proxy
definitions. The use of parent and child object definitions, along with
inner object definitions, can result in much cleaner and more concise
proxy definitions.</para>
<para>First a parent, template, object definition is created for the
proxy:</para>
<programlisting language="myxml">&lt;object id="txProxyTemplate" abstract="true"
type="Spring.Transaction.Interceptor.TransactionProxyFactoryObject, Spring.Data"&gt;
&lt;property name="PlatformTransactionManager" ref="adoTransactionManager"/&gt;
&lt;property name="TransactionAttributes"&gt;
&lt;name-values&gt;
&lt;add key="*" value="PROPAGATION_REQUIRED"/&gt;
&lt;/name-values&gt;
&lt;/property&gt;
&lt;/object&gt;</programlisting>
<para>This will never be instantiated itself, so may actually be
incomplete. Then each proxy which needs to be created is just a child
object definition, which wraps the target of the proxy as an inner
object definition, since the target will never be used on its own
anyway.</para>
<programlisting language="myxml">&lt;object name="testObjectManager" parent="txProxyTemplate"&gt;
&lt;property name="Target"&gt;
&lt;object type="Spring.Data.TestObjectManager, Spring.Data.Integration.Tests"&gt;
&lt;property name="TestObjectDao" ref="testObjectDao"/&gt;
&lt;/object&gt;
&lt;/property&gt;
&lt;/object&gt;</programlisting>
<para>It is of course possible to override properties from the parent
template, such as in this case, the transaction propagation
settings:</para>
<programlisting language="myxml">&lt;object name="testObjectManager" parent="txProxyTemplate"&gt;
&lt;property name="Target"&gt;
&lt;object type="Spring.Data.TestObjectManager, Spring.Data.Integration.Tests"&gt;
&lt;property name="TestObjectDao" ref="testObjectDao"/&gt;
&lt;/object&gt;
&lt;/property&gt;
&lt;property name="TransactionAttributes"&gt;
&lt;name-values&gt;
&lt;add key="Save*" value="PROPAGATION_REQUIRED"/&gt;
&lt;add key="Delete*" value="PROPAGATION_REQUIRED"/&gt;
&lt;add key="Find*" value="PROPAGATION_REQUIRED,readonly"/&gt;
&lt;/name-values&gt;
&lt;/property&gt;
&lt;/object&gt;</programlisting>
<para>Note that in the example above, we have explicitly marked the
parent object definition as abstract by using the abstract attribute, as
described previously, so that it may not actually ever be instantiated.
Application contexts (but not simple object factories) will by default
pre-instantiate all singletons. It is therefore important (at least for
singleton object) that if you have a (parent) object definition which
you intend to use only as a template, and this definition specifies a
class, you must make sure to set the abstract attribute to true,
otherwise the application context will actually try to pre-instantiate
it.</para>
</sect2>
</sect1>
<sect1 xml:id="aop-proxy-mechanism">
<title>Proxying mechanisms</title>
<para>Spring creates AOP proxies built at runtime through the use of the
TypeBuilder API.</para>
<para>Two types of proxies can be created, composition based or
inheritance based. If the target object implements at least one interface
then a composition based proxy will be created, otherwise an inheritance
based proxy will be created.</para>
<para>The composition based proxy is implemented by creating a type that
implements all the interfaces specified on the target object. The actual
class name of this dynamic type is 'GUID' like. A private field holds the
target object and the dynamic type implementation will first execute any
advice before or after making the target object method call on the target
object.</para>
<para>The inheritance based mechanism creates a dynamic type where that
inherits from the target type. This lets you downcast to the target type
if needed. Please note that in both cases a target method implementation
that calls other methods on the target object will not be advised. To
force inheritance based proxies you should either set the
<literal>ProxyTargetType</literal> to true property of a ProxyFactory or
set the XML namespace element <literal>proxy-target-type = true</literal>
when using an AOP schema based configuration.</para>
<note>
<para>An important alternative approach to inheritance based proxies is
disucssed in the next section.</para>
</note>
<para>In .NET 2.0 you can define the assembly level attribute,
InternalsVisibleTo, to allow access of internal interfaces/classes to
specified 'friend' assemblies. If you need to create an AOP proxy on an
internal class/interface add the following code, [assembly:
InternalsVisibleTo("Spring.Proxy")] and [assembly:
InternalsVisibleTo("Spring.DynamicReflection")] to your to AssemblyInfo
file.</para>
<sect2 xml:id="aop-inheritancebasedaopconfigurer">
<title>InheritanceBasedAopConfigurer</title>
<para>There is an important limitation in the inheritance based proxy as
described above, all methods that manipulate the state of the object
should be declared as virtual. Otherwise some method invocations get
directed to the private 'target' field member and others to the base
class. Winform object are an example of case where this approach does
not apply. To address this limitation, a new post-processing mechanism
was introduced in version 1.2 that creates a proxy type without the
private 'target' field. Interception advice is added directly in the
method body before invoking the base class method.</para>
<para>To use this new inheritance based proxy described in the note
above, declare an instance of the InheritanceBasedAopConfigurer, and
IObjectFactoryPostProcessor, in yoru configuraiton file. Here is an
example.</para>
<programlisting language="myxml">&lt;object type="Spring.Aop.Framework.AutoProxy.InheritanceBasedAopConfigurer, Spring.Aop"&gt;
&lt;property name="ObjectNames"&gt;
&lt;list&gt;
&lt;value&gt;Form*&lt;/value&gt;
&lt;value&gt;Control*&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;property name="InterceptorNames"&gt;
&lt;list&gt;
&lt;value&gt;debugInterceptor&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;/object&gt;
&lt;object id="debugInterceptor" type="AopPlay.DebugInterceptor, AopPlay"/&gt;</programlisting>
<para>This configuraiton style is similar to the autoproxy by name
approach described <link linkend="aop-nameautoproxy">here</link> and is
particuarly appropriate when you want to apply advice to WinForm
classes.</para>
</sect2>
</sect1>
<sect1 xml:id="aop-prog">
<title>Creating AOP Proxies Programatically with the ProxyFactory</title>
<para>It's easy to create AOP proxies Programatically using Spring.NET.
This enables you to use Spring.NET AOP without dependency on Spring.NET
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>
<programlisting language="csharp">ProxyFactory factory = new ProxyFactory(myBusinessInterfaceImpl);
factory.AddAdvice(myMethodInterceptor);
factory.AddAdvisor(myAdvisor);
IBusinessInterface tb = (IBusinessInterface) factory.GetProxy();</programlisting>
<para>The first step is to construct an object of type
<literal>Spring.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 interceptors or advisors, and manipulate them for the
life of the <literal>ProxyFactory.</literal></para>
<para>There are also convenience methods on
<literal>ProxyFactory</literal> (inherited from
<literal>AdvisedSupport</literal>) allowing you to add other advice types
such as before and throws advice. <literal>AdvisedSupport</literal> is the
superclass of both <literal>ProxyFactory</literal> and
<literal>ProxyFactoryObject</literal>.</para>
<note>
Integrating AOP proxy creation with the IoC framework is best practice in most applications. We recommend that you externalize configuration from .NET code with AOP, as in general.
</note>
</sect1>
<sect1 xml:id="aop-advised-objects">
<title>Manipulating Advised Objects</title>
<para>However you create AOP proxies, you can manipulate them using the
<literal>Spring.Aop.Framework.IAdvised</literal> interface. Any AOP proxy
can be cast to this interface, whatever other interfaces it implements.
This interface includes the following methods and properties:</para>
<programlisting language="csharp">public interface IAdvised
{
IAdvisor[] Advisors { get; }
IIntroductionAdvisor[] Introductions { get; }
void AddInterceptor(IInterceptor interceptor);
void AddInterceptor(int pos, IInterceptor interceptor);
void AddAdvisor(IAdvisor advisor);
void AddAdvisor(int pos, IAdvisor advisor);
void AddIntroduction(IIntroductionAdvisor advisor);
void AddIntroduction(int pos, IIntroductionAdvisor advisor);
int IndexOf(IAdvisor advisor);
int IndexOf(IIntroductionAdvisor advisor);
bool RemoveAdvisor(IAdvisor advisor);
void RemoveAdvisor(int index);
bool RemoveInterceptor(IInterceptor interceptor);
bool RemoveIntroduction(IIntroductionAdvisor advisor);
void RemoveIntroduction(int index);
void ReplaceIntroduction(int index, IIntroductionAdvisor advisor);
bool ReplaceAdvisor(IAdvisor a, IAdvisor b);
}</programlisting>
<para>The <literal>Advisors</literal> property will return an
<literal>IAdvisor</literal> for every advisor, interceptor or other advice
type that has been added to the factory. If you added an
<literal>IAdvisor</literal>, the returned advisor at this index will be
the object that you added. If you added an interceptor or other advice
type, Spring.NET will have wrapped this in an advisor with a
<literal>IPointcut</literal> that always returns <literal>true</literal>.
Thus if you added an <literal>IMethodInterceptor</literal>, the advisor
returned for this index will be a
<literal>DefaultPointcutAdvisor</literal> returning your
<literal>IMethodInterceptor</literal> and an <literal>IPointcut</literal>
that matches all types and methods.</para>
<para>The <literal>AddAdvisor()</literal> methods can be used to add any
<literal>IAdvisor</literal>. Usually this will be the generic
<literal>DefaultPointcutAdvisor</literal>, which can be used with any
advice or pointcut (but not for introduction).</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>
<remark>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.)</remark>
<para>Depending on how you created the proxy, you can usually set a
<literal>Frozen</literal> flag, in which case the
<literal>IAdvised</literal> <literal>IsFrozen</literal> property will
return <literal>true</literal>, 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.</para>
</sect1>
<sect1 xml:id="aop-autoproxy">
<title>Using the "autoproxy" facility</title>
<para>So far we've considered explicit creation of AOP proxies using a
<literal>ProxyFactoryObject</literal> or similar factory objects. For
applications that would like create many AOP proxies, say across all the
classes in a service layer, this approach can lead to a lengthy
configuration file. To simplify the creation of many AOP proxies Spring
provides "autoproxy" capabilities that will automatically proxy object
definitions based on higher level criteria that will group together
multiple objects as candidates to be proxied.</para>
<para>This functionality is built on Spring "object post-processor"
infrastructure, which enables modification of any object definition as the
container loads. Refer to <xref linkend="objects-factory-customizing" />
for general information on object post-processors.</para>
<para>In this model, you set up some special object definitions in your
XML object definition file configuring the auto proxy infrastructure. This
allows you just to declare the targets eligible for autoproxying: you
don't need to use <literal>ProxyFactoryObject</literal>.</para>
<itemizedlist>
<listitem>
<para>Using an autoproxy creator that refers to specific objects 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
attributes.</para>
</listitem>
</itemizedlist>
<para>Autoproxying in general has the advantage of making it impossible
for callers or dependencies to obtain an un-advised object. Calling
GetObject("MyBusinessObject1") on an ApplicationContext will return an AOP
proxy, not the target business object. The "inline object" idiom shown
earlier in <xref linkend="aop-proxying-interfaces" /> also offers this
benefit.)</para>
<sect2 xml:id="aop-autoproxy-choices">
<title>Autoproxy object definitions</title>
<para>The namespace <literal>Spring.Aop.Framework.AutoProxy</literal>
provides generic autoproxy infrastructure, should you choose to write
your own autoproxy implementations, as well as several out-of-the-box
implementations. Two implementations are provided,
<literal>ObjectNameAutoProxyCreator</literal> and
<literal>DefaultAdvisorAutoProxyCreator</literal>. These are discussed
in the following sections.</para>
<sect3 xml:id="aop-nameautoproxy">
<title>ObjectNameAutoProxyCreator</title>
<para>The <literal>ObjectNameAutoProxyCreator</literal> automatically
creates AOP proxies for object with names matching literal values or
wildcards. The pattern matching expressions supported are of the form
"*name", "name*", and "*name*" and exact name matching, i.e. "name".
The following simple classes are used to demonstrate this autoproxy
functionality.</para>
<programlisting language="csharp">public enum Language
{
English = 1,
Portuguese = 2,
Italian = 3
}
public interface IHelloWorldSpeaker
{
void SayHello();
}
public class HelloWorldSpeaker : IHelloWorldSpeaker
{
private Language language;
public Language Language
{
set { language = value; }
get { return language; }
}
public void SayHello()
{
switch (language)
{
case Language.English:
Console.WriteLine("Hello World!");
break;
case Language.Portuguese:
Console.WriteLine("Oi Mundo!");
break;
case Language.Italian:
Console.WriteLine("Ciao Mondo!");
break;
}
}
}
public class DebugInterceptor : IMethodInterceptor
{
public object Invoke(IMethodInvocation invocation)
{
Console.WriteLine("Before: " + invocation.Method.ToString());
object rval = invocation.Proceed();
Console.WriteLine("After: " + invocation.Method.ToString());
return rval;
}
}</programlisting>
<para>The following XML is used to automatically create an AOP proxy
and apply a Debug interceptor to object definitions whose names match
"English*" and "PortugueseSpeaker".</para>
<programlisting language="myxml">&lt;object id="ProxyCreator" type="Spring.Aop.Framework.AutoProxy.ObjectNameAutoProxyCreator, Spring.Aop"&gt;
&lt;property name="ObjectNames"&gt;
&lt;list&gt;
&lt;value&gt;English*&lt;/value&gt;
&lt;value&gt;PortugeseSpeaker&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;property name="InterceptorNames"&gt;
&lt;list&gt;
&lt;value&gt;debugInterceptor&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;/object&gt;
&lt;object id="debugInterceptor" type="AopPlay.DebugInterceptor, AopPlay"/&gt;
&lt;object id="EnglishSpeakerOne" type="AopPlay.HelloWorldSpeaker, AopPlay"&gt;
&lt;property name="Language" value="English"/&gt;
&lt;/object&gt;
&lt;object id="EnglishSpeakerTwo" type="AopPlay.HelloWorldSpeaker, AopPlay"&gt;
&lt;property name="Language" value="English"/&gt;
&lt;/object&gt;
&lt;object id="PortugeseSpeaker" type="AopPlay.HelloWorldSpeaker, AopPlay"&gt;
&lt;property name="Language" value="Portuguese"/&gt;
&lt;/object&gt;
&lt;object id="ItalianSpeakerOne" type="AopPlay.HelloWorldSpeaker, AopPlay"&gt;
&lt;property name="Language" value="Italian"/&gt;
&lt;/object&gt;</programlisting>
<para>As with <literal>ProxyFactoryObject</literal>, there is an
InterceptorNames 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>The same advice will be applied to all matching objects. Note
that if advisors are used (rather than the interceptor in the above
example), the pointcuts may apply differently to different
objects.</para>
<para>Running the following simple program demonstrates the
application of the AOP interceptor.</para>
<programlisting language="csharp">
IApplicationContext ctx = ContextRegistry.GetContext();
IDictionary speakerDictionary = ctx.GetObjectsOfType(typeof(IHelloWorldSpeaker));
foreach (DictionaryEntry entry in speakerDictionary)
{
string name = (string)entry.Key;
IHelloWorldSpeaker worldSpeaker = (IHelloWorldSpeaker)entry.Value;
Console.Write(name + " says; ");
worldSpeaker.SayHello();
}
</programlisting>
<para>The output is shown below</para>
<programlisting language="csharp">ItalianSpeakerOne says; Ciao Mondo!
EnglishSpeakerTwo says; Before: Void SayHello()
Hello World!
After: Void SayHello()
PortugeseSpeaker says; Before: Void SayHello()
Oi Mundo!
After: Void SayHello()
EnglishSpeakerOne says; Before: Void SayHello()
Hello World!
After: Void SayHello()</programlisting>
</sect3>
<sect3 xml:id="aop-advisorautoproxy">
<title>DefaultAdvisorAutoProxyCreator</title>
<para>A more general and extremely powerful auto proxy creator is
<literal>DefaultAdvisorAutoProxyCreator</literal>. This will
automatically apply eligible advisors in the current application
context, without the need to include specific object names in the
autoproxy advisor's object definition. It offers the same merit of
consistent configuration and avoidance of duplication as
<literal>ObjectNameAutoProxyCreator</literal>.</para>
<para>Using this mechanism involves:</para>
<itemizedlist>
<listitem>
<para>Specifying a
<literal>DefaultAdvisorAutoProxyCreator</literal> object
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 object 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 object defined in the
application context.</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.</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 following example demonstrates the use of
<literal>DefaultAdvisorAutoProxyCreator</literal>. Expanding on the
previous example code used to demonstrate
<literal>ObjectNameAutoProxyCreator</literal> we will add a new class,
<literal>SpeakerDao</literal>, that acts as a Data Access Object to
find and store <literal>IHelloWorldSpeaker</literal> objects.
<programlisting language="csharp">
public interface ISpeakerDao
{
IList FindAll();
IHelloWorldSpeaker Save(IHelloWorldSpeaker speaker);
}
public class SpeakerDao : ISpeakerDao
{
public System.Collections.IList FindAll()
{
Console.WriteLine("Finding speakers...");
// just a demo...fake the retrieval.
Thread.Sleep(10000);
HelloWorldSpeaker speaker = new HelloWorldSpeaker();
speaker.Language = Language.Portuguese;
IList list = new ArrayList();
list.Add(speaker);
return list;
}
public IHelloWorldSpeaker Save(IHelloWorldSpeaker speaker)
{
Console.WriteLine("Saving speaker...");
// just a demo...not really saving...
return speaker;
}
}
</programlisting></para>
The XML configuration specifies two Advisors, that is, the combination of advice (the behavior to add) and a pointcut (where the behavior should be applied). A
<literal>RegularExpressionMethodPointcutAdvisor</literal>
is used as a convenience to specify the pointcut as a regular expression that matches methods names. Other pointcuts of your own creation could be used, in which case a
<literal>DefaultPointcutAdvisor</literal>
would be used to define the Advisor. The object definitions for these advisors, advice, and SpeakerDao object are shown below
<programlisting language="myxml">&lt;object id="SpeachAdvisor" type="Spring.Aop.Support.RegularExpressionMethodPointcutAdvisor, Spring.Aop"&gt;
&lt;property name="advice" ref="debugInterceptor"/&gt;
&lt;property name="patterns"&gt;
&lt;list&gt;
&lt;value&gt;.*Say.*&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;/object&gt;
&lt;object id="AdoAdvisor" type="Spring.Aop.Support.RegularExpressionMethodPointcutAdvisor, Spring.Aop"&gt;
&lt;property name="advice" ref="timingInterceptor"/&gt;
&lt;property name="patterns"&gt;
&lt;list&gt;
&lt;value&gt;.*Find.*&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;/object&gt;
// Advice
&lt;object id="debugInterceptor" type="AopPlay.DebugInterceptor, AopPlay"/&gt;
&lt;object id="timingInterceptor" type="AopPlay.TimingInterceptor, AopPlay"/&gt;
// Speaker DAO Object - has 'FindAll' Method.
&lt;object id="speakerDao" type="AopPlay.SpeakerDao, AopPlay"/&gt;
// HelloWorldSpeaker objects as previously listed.
</programlisting>
<para>Adding an instance of
<literal>DefaultAdvisorAutoProxyCreator</literal> to the configuration
file <programlisting language="myxml">&lt;object id="ProxyCreator" type="Spring.Aop.Framework.AutoProxy.DefaultAdvisorAutoProxyCreator, Spring.Aop"/&gt;</programlisting>
will apply the debug interceptor on all objects in the context that
have a method that contains the text "Say" and apply the timing
interceptor on objects in the context that have a method that contains
the text "Find". Running the following code demonstrates this
behavior. Note that the "Save" method of SpeakerDao does not have any
advice applied to it.</para>
<programlisting language="csharp">
IApplicationContext ctx = ContextRegistry.GetContext();
IDictionary speakerDictionary = ctx.GetObjectsOfType(typeof(IHelloWorldSpeaker));
foreach (DictionaryEntry entry in speakerDictionary)
{
string name = (string)entry.Key;
IHelloWorldSpeaker worldSpeaker = (IHelloWorldSpeaker)entry.Value;
Console.Write(name + " says; ");
worldSpeaker.SayHello();
}
ISpeakerDao dao = (ISpeakerDao)ctx.GetObject("speakerDao");
IList speakerList = dao.FindAll();
IHelloWorldSpeaker speaker = dao.Save(new HelloWorldSpeaker());
</programlisting>
<para>This produces the following output</para>
<programlisting language="csharp">
ItalianSpeakerOne says; Before: Void SayHello()
Ciao Mondo!
After: Void SayHello()
EnglishSpeakerTwo says; Before: Void SayHello()
Hello World!
After: Void SayHello()
PortugeseSpeaker says; Before: Void SayHello()
Oi Mundo!
After: Void SayHello()
EnglishSpeakerOne says; Before: Void SayHello()
Hello World!
After: Void SayHello()
Finding speakers...
Elapsed time = 00:00:10.0154745
Saving speaker...
</programlisting>
<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>Spring.Core.IOrdered</literal> interface to
ensure correct ordering if this is an issue. The default is
unordered.</para>
</sect3>
<sect3>
<title>PointcutFilteringAutoProxyCreator</title>
<para>An AutoProxyCreator that identified objects to proxy by matching
a specified <literal>IPointcut</literal>.</para>
</sect3>
<sect3>
<title>TypeNameAutoProxyCreator</title>
<para>An AutoProxyCreator that identifies objects to proxy by matching
their <literal>Type.FullName</literal> against a list of
patterns.</para>
</sect3>
<sect3>
<title>AttributeAutoProxyCreator</title>
<para>An AutoProxyCreator, that identifies objects to be proxied by
checking any System.Attribute defined on a given type and that types
interfaces.</para>
</sect3>
<sect3>
<title>AbstractFilteringAutoProxyCreator</title>
<para>The base class for AutoProxyCreator implementations that mark
objects eligible for proxying based on arbitrary criteria.</para>
</sect3>
<sect3>
<title>AbstractAutoProxyCreator</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>
</sect3>
</sect2>
<sect2 xml:id="aop-autoproxy-metadata">
<title>Using attribute-driven auto-proxying</title>
<para>A particularly important type of autoproxying is driven by
attributes. The programming model is similar to using Enterprise
Services with ServicedComponents.</para>
<para>In this case, you use the
<literal>DefaultAdvisorAutoProxyCreator</literal>, in combination with
Advisors that understand attributes. The Advisor pointcut is identified
by the presence of .NET attribute in the source code and it is
configured via the data and/or methods of the attribute. This is a
powerful alternative to identifying the advisor pointcut and advice
configuration through traditional property configuration, either
programmatic or through XML based configuration.</para>
<para>Several of the aspect provided with Spring use attribute driven
autoproxying. The most prominent example is <link
linkend="transaction">Transaction</link> support.</para>
</sect2>
</sect1>
<sect1 xml:id="aop-namespace">
<title>Using AOP Namespace</title>
<para>The AOP namespace allows you to define an advisor, i.e pointcut + 1
piece of advice, in a more declarative manner. Under the covers the
DefaultAdvisorAutoProxyCreator is being used. Here is an example,</para>
<programlisting language="myxml">&lt;objects xmlns="http://www.springframework.net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.net/aop"&gt;
&lt;aop:config&gt;
&lt;aop:advisor id="getDescriptionAdvisor" pointcut-ref="getDescriptionCalls" advice-ref="getDescriptionCounter"/&gt;
&lt;/aop:config&gt;
&lt;object id="getDescriptionCalls"
type="Spring.Aop.Support.SdkRegularExpressionMethodPointcut, Spring.Aop"&gt;
&lt;property name="patterns"&gt;
&lt;list&gt;
&lt;value&gt;.*GetDescription.*&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;/object&gt;
&lt;object id="getDescriptionCounter" type="Spring.Aop.Framework.CountingBeforeAdvice, Spring.Aop.Tests"/&gt;
&lt;object name="testObject" type="Spring.Objects.TestObject, Spring.Core.Tests"/&gt;
&lt;/objects&gt;</programlisting>
<para>In this example, the TestObject, which implements the interface
ITestObject, is having AOP advice applied to it. The method
GetDescription() is specified as a regular expression pointcut. The
aop:config tag and subsequent child tag, aop:advisor, brings together the
pointcut with the advice.</para>
<para>In order to have Spring.NET recognise the aop namespace, you need to
declare the namespace parser in the main Spring.NET configuration section.
For convenience this is shown below. Please refer to the section titled
<link lang="" linkend="context-configuration">context configuration</link>
for more extensive information..</para>
<para><programlisting language="myxml">&lt;configuration&gt;
&lt;configSections&gt;
&lt;sectionGroup name="spring"&gt;
&lt;section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/&gt;
&lt;section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" /&gt;
&lt;section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core"/&gt;
&lt;/sectionGroup&gt;
&lt;/configSections&gt;
&lt;spring&gt;
&lt;parsers&gt;
&lt;parser type="Spring.Aop.Config.AopNamespaceParser, Spring.Aop" /&gt;
&lt;/parsers&gt;
&lt;context&gt;
&lt;resource uri="config://spring/objects"/&gt;
&lt;/context&gt;
&lt;objects xmlns="http://www.springframework.net"&gt;
...
&lt;/objects&gt;
&lt;/spring&gt;
&lt;/configuration&gt;
</programlisting></para>
</sect1>
<sect1 xml:id="aop-targetsource">
<title>Using TargetSources</title>
<para>Spring.NET offers the concept of a
<emphasis>TargetSource</emphasis>, expressed in the
<literal>Spring.Aop.ITargetSource</literal> interface. This interface is
responsible for returning the "target object" implementing the joinpoint.
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.NET 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.NET,
and how you can use them.</para>
<remark>When using a custom target source, your target will usually need
to be a prototype rather than a singleton object definition. This allows
Spring.NET to create a new target instance when required.</remark>
<sect2 xml:id="aop-ts-swap">
<title>Hot swappable target sources</title>
<para>The
<literal>org.Spring.NETframework.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 thread safe.</para>
<para>You can change the target via the <literal>swap()</literal> method
on HotSwappableTargetSource as follows:</para>
<para><programlisting language="csharp">HotSwappableTargetSource swapper =
(HotSwappableTargetSource) objectFactory.GetObject("swapper");
object oldTarget = swapper.swap(newTarget);</programlisting></para>
<para>The XML definitions required look as follows:</para>
<programlisting language="myxml">&lt;object id="initialTarget" type="MyCompany.OldTarget, MyCompany"&gt;
&lt;/object&gt;
&lt;object id="swapper"
type="Spring.Aop.Target.HotSwappableTargetSource, Spring.Aop"&gt;
&lt;constructor-arg&gt;&lt;ref local="initialTarget"/&gt;&lt;/constructor-arg&gt;
&lt;/object&gt;
&lt;object id="swappable"
type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop"
&gt;
&lt;property name="targetSource"&gt;
&lt;ref local="swapper"/&gt;
&lt;/property&gt;
&lt;/object&gt;</programlisting>
<para>The above <literal>swap()</literal> call changes the target of the
swappable object. Clients who hold a reference to that object 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>
</sect2>
<sect2 xml:id="aop-ts-pool">
<title>Pooling target sources</title>
<para>Using a pooling target source provides a programming model 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.NET pooling and pooling in
.NET Enterprise Services pooling is that Spring.NET pooling can be
applied to any POCO. (Plain old CLR object). As with Spring.NET in
general, this service can be applied in a non-invasive way.</para>
<para>Spring.NET provides out-of-the-box support using a pooling
implementation based on Jakarta Commons Pool 1.1, which provides a
fairly efficient pooling implementation. It's also possible to subclass
<literal>Spring.Aop.Target.AbstractPoolingTargetSource</literal> to
support any other pooling API.</para>
<para>Sample configuration is shown below:</para>
<programlisting language="myxml">&lt;object id="businessObjectTarget" type="MyCompany.MyBusinessObject, MyCompany" singleton="false"&gt;
... properties omitted
&lt;/object&gt;
&lt;object id="poolTargetSource" type="Spring.Aop.Target.SimplePoolTargetSource, Spring.Aop"&gt;
&lt;property name="targetObjectName" value="businessObjectTarget"/&gt;
&lt;property name="maxSize" value="25"/&gt;
&lt;/object&gt;
&lt;object id="businessObject" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop"&gt;
&lt;property name="targetSource" ref="poolTargetSource"/&gt;
&lt;property name="interceptorNames" value="myInterceptor"/&gt;
&lt;/object&gt;</programlisting>
<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 SDK
documentation for <literal>AbstractPoolingTargetSource</literal> and the
concrete subclass you wish to use for information about it's 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.NET so as to be able to cast any
pooled object to the <literal>Spring.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>
<programlisting language="myxml">&lt;object id="poolConfigAdvisor"
type="Spring.Object.Factory.Config.MethodInvokingFactoryObject, Spring.Aop"&gt;
&lt;property name="target" ref="poolTargetSource" /&gt;
&lt;property name="targetMethod" value="getPoolingConfigMixin" /&gt;
&lt;/object&gt;</programlisting>
<para>This advisor is obtained by calling a convenience method on the
<literal>AbstractPoolingTargetSource</literal> class, hence the use of
<literal>MethodInvokingFactoryObject</literal>. This advisor's name
(<literal>'poolConfigAdvisor'</literal> here) must be in the list of
interceptor names in the <literal>ProxyFactoryObject</literal> exposing
the pooled object.</para>
<para>The cast will look as follows:</para>
<programlisting language="csharp">PoolingConfig conf = (PoolingConfig) objectFactory.GetObject("businessObject");
Console.WriteLine("Max pool size is " + conf.getMaxSize());</programlisting>
<remark>Pooling stateless service objects is not usually necessary. We
don't believe it should be the default choice, as most stateless objects
are naturally threadsafe, and instance pooling is problematic if
resources are cached.</remark>
<para>Simpler pooling is available using autoproxying. It's possible to
set the TargetSources used by any autoproxy creator.</para>
</sect2>
<sect2 xml: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
may not be high, 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.
(the name of the definition has also been changed, for clarity.)</para>
<programlisting language="myxml">&lt;object id="prototypeTargetSource"
type="Spring.Aop.Target.PrototypeTargetSource, Spring.Aop"&gt;
&lt;property name="targetObjectName" value="businessObject" /&gt;
&lt;/object&gt;</programlisting>
<para>There is only one property: the name of the target object.
Inheritance is used in the TargetSource implementations to ensure
consistent naming. As with the pooling target source, the target object
must be a prototype object definition, the singleton property of the
target should be set to false.</para>
</sect2>
<sect2>
<title>ThreadLocal target sources</title>
<para>ThreadLocal target sources are useful if you need an object to be
created for each incoming request (per thread that is). The concept of a
ThreadLocal provides a facility to transparently store resource
alongside a thread. Setting up a ThreadLocalTargetSource is pretty much
the same as was explained for the other types of target source:</para>
<programlisting language="myxml">&lt;object id="threadlocalTargetSource"
type="Spring.Aop.Target.ThreadLocalTargetSource, Spring.Aop"&gt;
&lt;property name="targetObjectName" value="businessObject" /&gt;
&lt;/object&gt;</programlisting>
</sect2>
</sect1>
<sect1 xml:id="aop-extensibility">
<title>Defining new Advice types</title>
<para>Spring.NET 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 interception around, before,
throws, and after returning advice, which are supported out of the
box.</para>
<para>The <literal>Spring.Aop.Framework.Adapter</literal> package is an
SPI (Service Provider Interface) package allowing support for new custom
advice types to be added without changing the core framework. The only
constraint on a custom Advice type is that it must implement the
<literal>AopAlliance.Aop.IAdvice</literal> tag interface.</para>
<para>Please refer to the <literal>Spring.Aop.Framework.Adapter</literal>
namespace documentation for further information.</para>
</sect1>
<sect1 xml:id="aop-futher-reading">
<title>Further reading and resources</title>
<para>The Spring.NET team recommends the excellent <emphasis>AspectJ in
Action</emphasis> by Ramnivas Laddad (Manning, 2003) for an introduction
to AOP.</para>
<para>If you are interested in more advanced capabilities of Spring.NET
AOP, take a look at the test suite as it illustrates advanced features not
discussed in this document.</para>
</sect1>
</chapter>