/
aop.xml
2570 lines (2018 loc) · 110 KB
/
aop.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?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
{