2569 lines
110 KiB
XML
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"><object id="settersAndAbsquatulatePointcut"
|
|
type="Spring.Aop.Support.SdkRegularExpressionMethodPointcut, Spring.Aop">
|
|
<property name="patterns">
|
|
<list>
|
|
<value>.*set.*</value>
|
|
<value>.*absquatulate</value>
|
|
</list>
|
|
</property>
|
|
</object></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"><object id="settersAndAbsquatulateAdvisor"
|
|
type="Spring.Aop.Support.RegularExpressionMethodPointcutAdvisor, Spring.Aop">
|
|
<property name="advice">
|
|
<ref local="objectNameOfAopAllianceInterceptor"/>
|
|
</property>
|
|
<property name="patterns">
|
|
<list>
|
|
<value>.*set.*</value>
|
|
<value>.*absquatulate</value>
|
|
</list>
|
|
</property>
|
|
</object></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"><object id="cachePointcut" type="Spring.Aop.Support.AttributeMatchMethodPointcut, Spring.Aop">
|
|
<property name="Attribute" value="Spring.Attributes.CacheAttribute, Spring.Core"/>
|
|
</object></programlisting></para>
|
|
|
|
<para>This can be used with a
|
|
<literal>DefaultPointcutAdvisor</literal> as shown
|
|
below<programlisting language="myxml"><object id="cacheAspect" type="Spring.Aop.Support.DefaultPointcutAdvisor, Spring.Aop">
|
|
<property name="Pointcut">
|
|
<object type="Spring.Aop.Support.AttributeMatchMethodPointcut, Spring.Aop">
|
|
<property name="Attribute" value="Spring.Attributes.CacheAttribute, Spring.Core"/>
|
|
</object>
|
|
</property>
|
|
<property name="Advice" ref="aspNetCacheAdvice"/>
|
|
</object> </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"><object id="AspNetCacheAdvice" type="Spring.Aop.Support.AttributeMatchMethodPointcutAdvisor, Spring.Aop">
|
|
<property name="advice">
|
|
<object type="Aspect.AspNetCacheAdvice, Aspect"/>
|
|
</property>
|
|
<property name="attribute" value="Framework.AspNetCacheAttribute, Framework" />
|
|
</object></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"><object id="personTarget" type="MyCompany.MyApp.Person, MyCompany">
|
|
<property name="name" value="Tony"/>
|
|
<property name="age" value="51"/>
|
|
</object>
|
|
|
|
<object id="myCustomInterceptor" type="MyCompany.MyApp.MyCustomInterceptor, MyCompany">
|
|
<property name="customProperty" value="configuration string"/>
|
|
</object>
|
|
|
|
<object id="debugInterceptor" type="Spring.Aop.Advice.DebugAdvice, Spring.Aop">
|
|
</object>
|
|
|
|
<object id="person" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop">
|
|
|
|
<property name="proxyInterfaces" value="MyCompany.MyApp.IPerson"/>
|
|
|
|
<property name="target" ref="personTarget"/>
|
|
|
|
<property name="interceptorNames">
|
|
<list>
|
|
<value>debugInterceptor</value>
|
|
<value>myCustomInterceptor</value>
|
|
</list>
|
|
</property>
|
|
|
|
</object></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"><object id="personUser" type="MyCompany.MyApp.PersonUser, MyCompany">
|
|
<property name="person" ref="person"/>
|
|
</object></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"><object id="myCustomInterceptor" type="MyCompany.MyApp.MyCustomInterceptor, MyCompany">
|
|
<property name="customProperty" value="configuration string"/>
|
|
</object>
|
|
|
|
<object id="debugInterceptor" type="Spring.Aop.Advice.DebugAdvice, Spring.Aop">
|
|
</object>
|
|
|
|
<object id="person" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop">
|
|
|
|
<property name="proxyInterfaces" value="MyCompany.MyApp.IPerson"/>
|
|
|
|
<property name="target">
|
|
<!-- Instead of using a reference to target, just use an inline object -->
|
|
<object type="MyCompany.MyApp.Person, MyCompany">
|
|
<property name="name" value="Tony"/>
|
|
<property name="age" value="51"/>
|
|
</object>
|
|
</property>
|
|
|
|
<property name="interceptorNames">
|
|
<list>
|
|
<value>debugInterceptor</value>
|
|
<value>myCustomInterceptor</value>
|
|
</list>
|
|
</property>
|
|
|
|
</object></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">
|
|
<!-- create the object to reference -->
|
|
<object id="RealObjectTarget" type="MyRealObject" singleton="false"/>
|
|
<!-- create the proxied object for everyone to use-->
|
|
<object id="MyObject" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop">
|
|
<property name="proxyInterfaces" value="MyInterface" />
|
|
<property name="isSingleton" value="false"/>
|
|
<property name="targetName" value="RealObjectTarget" />
|
|
</object>
|
|
</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"><object id="txProxyTemplate" abstract="true"
|
|
type="Spring.Transaction.Interceptor.TransactionProxyFactoryObject, Spring.Data">
|
|
|
|
<property name="PlatformTransactionManager" ref="adoTransactionManager"/>
|
|
<property name="TransactionAttributes">
|
|
<name-values>
|
|
<add key="*" value="PROPAGATION_REQUIRED"/>
|
|
</name-values>
|
|
</property>
|
|
</object></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"><object name="testObjectManager" parent="txProxyTemplate">
|
|
<property name="Target">
|
|
<object type="Spring.Data.TestObjectManager, Spring.Data.Integration.Tests">
|
|
<property name="TestObjectDao" ref="testObjectDao"/>
|
|
</object>
|
|
</property>
|
|
</object></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"><object name="testObjectManager" parent="txProxyTemplate">
|
|
<property name="Target">
|
|
<object type="Spring.Data.TestObjectManager, Spring.Data.Integration.Tests">
|
|
<property name="TestObjectDao" ref="testObjectDao"/>
|
|
</object>
|
|
</property>
|
|
<property name="TransactionAttributes">
|
|
<name-values>
|
|
<add key="Save*" value="PROPAGATION_REQUIRED"/>
|
|
<add key="Delete*" value="PROPAGATION_REQUIRED"/>
|
|
<add key="Find*" value="PROPAGATION_REQUIRED,readonly"/>
|
|
</name-values>
|
|
</property>
|
|
</object></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"><object type="Spring.Aop.Framework.AutoProxy.InheritanceBasedAopConfigurer, Spring.Aop">
|
|
<property name="ObjectNames">
|
|
<list>
|
|
<value>Form*</value>
|
|
<value>Control*</value>
|
|
</list>
|
|
</property>
|
|
<property name="InterceptorNames">
|
|
<list>
|
|
<value>debugInterceptor</value>
|
|
</list>
|
|
</property>
|
|
</object>
|
|
|
|
<object id="debugInterceptor" type="AopPlay.DebugInterceptor, AopPlay"/></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"><object id="ProxyCreator" type="Spring.Aop.Framework.AutoProxy.ObjectNameAutoProxyCreator, Spring.Aop">
|
|
<property name="ObjectNames">
|
|
<list>
|
|
<value>English*</value>
|
|
<value>PortugeseSpeaker</value>
|
|
</list>
|
|
</property>
|
|
<property name="InterceptorNames">
|
|
<list>
|
|
<value>debugInterceptor</value>
|
|
</list>
|
|
</property>
|
|
</object>
|
|
|
|
<object id="debugInterceptor" type="AopPlay.DebugInterceptor, AopPlay"/>
|
|
|
|
<object id="EnglishSpeakerOne" type="AopPlay.HelloWorldSpeaker, AopPlay">
|
|
<property name="Language" value="English"/>
|
|
</object>
|
|
|
|
<object id="EnglishSpeakerTwo" type="AopPlay.HelloWorldSpeaker, AopPlay">
|
|
<property name="Language" value="English"/>
|
|
</object>
|
|
|
|
<object id="PortugeseSpeaker" type="AopPlay.HelloWorldSpeaker, AopPlay">
|
|
<property name="Language" value="Portuguese"/>
|
|
</object>
|
|
|
|
<object id="ItalianSpeakerOne" type="AopPlay.HelloWorldSpeaker, AopPlay">
|
|
<property name="Language" value="Italian"/>
|
|
</object></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"><object id="SpeachAdvisor" type="Spring.Aop.Support.RegularExpressionMethodPointcutAdvisor, Spring.Aop">
|
|
|
|
<property name="advice" ref="debugInterceptor"/>
|
|
<property name="patterns">
|
|
<list>
|
|
<value>.*Say.*</value>
|
|
</list>
|
|
</property>
|
|
|
|
</object>
|
|
|
|
<object id="AdoAdvisor" type="Spring.Aop.Support.RegularExpressionMethodPointcutAdvisor, Spring.Aop">
|
|
|
|
<property name="advice" ref="timingInterceptor"/>
|
|
<property name="patterns">
|
|
<list>
|
|
<value>.*Find.*</value>
|
|
</list>
|
|
</property>
|
|
|
|
</object>
|
|
|
|
// Advice
|
|
<object id="debugInterceptor" type="AopPlay.DebugInterceptor, AopPlay"/>
|
|
|
|
<object id="timingInterceptor" type="AopPlay.TimingInterceptor, AopPlay"/>
|
|
|
|
// Speaker DAO Object - has 'FindAll' Method.
|
|
<object id="speakerDao" type="AopPlay.SpeakerDao, AopPlay"/>
|
|
|
|
// HelloWorldSpeaker objects as previously listed.
|
|
|
|
</programlisting>
|
|
|
|
|
|
|
|
<para>Adding an instance of
|
|
<literal>DefaultAdvisorAutoProxyCreator</literal> to the configuration
|
|
file <programlisting language="myxml"><object id="ProxyCreator" type="Spring.Aop.Framework.AutoProxy.DefaultAdvisorAutoProxyCreator, Spring.Aop"/></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"><objects xmlns="http://www.springframework.net"
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
xmlns:aop="http://www.springframework.net/aop">
|
|
|
|
<aop:config>
|
|
|
|
<aop:advisor id="getDescriptionAdvisor" pointcut-ref="getDescriptionCalls" advice-ref="getDescriptionCounter"/>
|
|
|
|
</aop:config>
|
|
|
|
<object id="getDescriptionCalls"
|
|
type="Spring.Aop.Support.SdkRegularExpressionMethodPointcut, Spring.Aop">
|
|
<property name="patterns">
|
|
<list>
|
|
<value>.*GetDescription.*</value>
|
|
</list>
|
|
</property>
|
|
</object>
|
|
|
|
|
|
<object id="getDescriptionCounter" type="Spring.Aop.Framework.CountingBeforeAdvice, Spring.Aop.Tests"/>
|
|
|
|
<object name="testObject" type="Spring.Objects.TestObject, Spring.Core.Tests"/>
|
|
|
|
|
|
</objects></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"><configuration>
|
|
|
|
<configSections>
|
|
<sectionGroup name="spring">
|
|
|
|
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/>
|
|
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
|
|
|
|
<section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core"/>
|
|
|
|
</sectionGroup>
|
|
</configSections>
|
|
|
|
<spring>
|
|
|
|
<parsers>
|
|
<parser type="Spring.Aop.Config.AopNamespaceParser, Spring.Aop" />
|
|
</parsers>
|
|
|
|
|
|
<context>
|
|
<resource uri="config://spring/objects"/>
|
|
</context>
|
|
|
|
<objects xmlns="http://www.springframework.net">
|
|
...
|
|
</objects>
|
|
|
|
</spring>
|
|
|
|
</configuration>
|
|
</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"><object id="initialTarget" type="MyCompany.OldTarget, MyCompany">
|
|
</object>
|
|
|
|
<object id="swapper"
|
|
type="Spring.Aop.Target.HotSwappableTargetSource, Spring.Aop">
|
|
<constructor-arg><ref local="initialTarget"/></constructor-arg>
|
|
</object>
|
|
|
|
<object id="swappable"
|
|
type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop"
|
|
>
|
|
<property name="targetSource">
|
|
<ref local="swapper"/>
|
|
</property>
|
|
</object></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"><object id="businessObjectTarget" type="MyCompany.MyBusinessObject, MyCompany" singleton="false">
|
|
... properties omitted
|
|
</object>
|
|
|
|
<object id="poolTargetSource" type="Spring.Aop.Target.SimplePoolTargetSource, Spring.Aop">
|
|
<property name="targetObjectName" value="businessObjectTarget"/>
|
|
<property name="maxSize" value="25"/>
|
|
</object>
|
|
|
|
<object id="businessObject" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop">
|
|
<property name="targetSource" ref="poolTargetSource"/>
|
|
<property name="interceptorNames" value="myInterceptor"/>
|
|
</object></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"><object id="poolConfigAdvisor"
|
|
type="Spring.Object.Factory.Config.MethodInvokingFactoryObject, Spring.Aop">
|
|
<property name="target" ref="poolTargetSource" />
|
|
<property name="targetMethod" value="getPoolingConfigMixin" />
|
|
</object></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"><object id="prototypeTargetSource"
|
|
type="Spring.Aop.Target.PrototypeTargetSource, Spring.Aop">
|
|
<property name="targetObjectName" value="businessObject" />
|
|
</object></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"><object id="threadlocalTargetSource"
|
|
type="Spring.Aop.Target.ThreadLocalTargetSource, Spring.Aop">
|
|
<property name="targetObjectName" value="businessObject" />
|
|
</object></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>
|