forked from spring-projects/spring-framework
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathaop-api.xml
More file actions
1970 lines (1556 loc) · 88 KB
/
aop-api.xml
File metadata and controls
1970 lines (1556 loc) · 88 KB
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"?>
<chapter xml:id="aop-api"
xmlns="http://docbook.org/ns/docbook" version="5.0"
xmlns:xl="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://docbook.org/ns/docbook http://www.docbook.org/xml/5.0/xsd/docbook.xsd
http://www.w3.org/1999/xlink http://www.docbook.org/xml/5.0/xsd/xlink.xsd">
<title>Spring AOP APIs</title>
<section xml:id="aop-api-introduction">
<title>Introduction</title>
<para>The previous chapter described the Spring 2.0 and later version's
support for AOP using @AspectJ and schema-based aspect definitions. In
this chapter we discuss the lower-level Spring AOP APIs and the AOP
support used in Spring 1.2 applications. For new applications, we
recommend the use of the Spring 2.0 and later AOP support described in the
previous chapter, but when working with existing applications, or when
reading books and articles, you may come across Spring 1.2 style examples.
Spring 3.0 is backwards compatible with Spring 1.2 and everything
described in this chapter is fully supported in Spring 3.0.</para>
</section>
<section xml:id="aop-api-pointcuts">
<title>Pointcut API in Spring</title>
<para>Let's look at how Spring handles the crucial pointcut
concept.</para>
<section xml:id="aop-api-concepts">
<title>Concepts</title>
<para>Spring's pointcut model enables pointcut reuse independent of
advice types. It's possible to target different advice using the same
pointcut.</para>
<para>The <literal>org.springframework.aop.Pointcut</literal> interface
is the central interface, used to target advices to particular classes
and methods. The complete interface is shown below:</para>
<programlisting language="java">public interface Pointcut {
ClassFilter getClassFilter();
MethodMatcher getMethodMatcher();
}</programlisting>
<para>Splitting the <interfacename>Pointcut</interfacename> interface
into two parts allows reuse of class and method matching parts, and
fine-grained composition operations (such as performing a "union" with
another method matcher).</para>
<para>The <interfacename>ClassFilter</interfacename> interface is used
to restrict the pointcut to a given set of target classes. If the
<literal>matches()</literal> method always returns true, all target
classes will be matched:</para>
<programlisting language="java">public interface ClassFilter {
boolean matches(Class clazz);
}</programlisting>
<para>The <interfacename>MethodMatcher</interfacename> interface is
normally more important. The complete interface is shown below:</para>
<programlisting language="java">public interface MethodMatcher {
boolean matches(Method m, Class targetClass);
boolean isRuntime();
boolean matches(Method m, Class targetClass, Object[] args);
}</programlisting>
<para>The <literal>matches(Method, Class) </literal>method is used to
test whether this pointcut will ever match a given method on a target
class. This evaluation can be performed when an AOP proxy is created, to
avoid the need for a test on every method invocation. If the 2-argument
matches method returns true for a given method, and the
<literal>isRuntime()</literal> method for the MethodMatcher returns
true, the 3-argument matches method will be invoked on every method
invocation. This enables a pointcut to look at the arguments passed to
the method invocation immediately before the target advice is to
execute.</para>
<para>Most MethodMatchers are static, meaning that their
<literal>isRuntime()</literal> method returns false. In this case, the
3-argument matches method will never be invoked.</para>
<tip>
<para>If possible, try to make pointcuts static, allowing the AOP
framework to cache the results of pointcut evaluation when an AOP
proxy is created.</para>
</tip>
</section>
<section xml:id="aop-api-pointcut-ops">
<title>Operations on pointcuts</title>
<para>Spring supports operations on pointcuts: notably,
<emphasis>union</emphasis> and <emphasis>intersection</emphasis>.</para>
<itemizedlist>
<listitem>
<para>Union means the methods that either pointcut matches.</para>
</listitem>
<listitem>
<para>Intersection means the methods that both pointcuts
match.</para>
</listitem>
<listitem>
<para>Union is usually more useful.</para>
</listitem>
<listitem>
<para>Pointcuts can be composed using the static methods in the
<emphasis>org.springframework.aop.support.Pointcuts</emphasis>
class, or using the <emphasis>ComposablePointcut</emphasis> class in
the same package. However, using AspectJ pointcut expressions is
usually a simpler approach.</para>
</listitem>
</itemizedlist>
</section>
<section xml:id="aop-api-pointcuts-aspectj">
<title>AspectJ expression pointcuts</title>
<para>Since 2.0, the most important type of pointcut used by Spring is
<literal>org.springframework.aop.aspectj.AspectJExpressionPointcut</literal>.
This is a pointcut that uses an AspectJ supplied library to parse an
AspectJ pointcut expression string.</para>
<para>See the previous chapter for a discussion of supported AspectJ
pointcut primitives.</para>
</section>
<section xml:id="aop-api-pointcuts-impls">
<title>Convenience pointcut implementations</title>
<para>Spring provides several convenient pointcut implementations. Some
can be used out of the box; others are intended to be subclassed in
application-specific pointcuts.</para>
<section xml:id="aop-api-pointcuts-static">
<title>Static pointcuts</title>
<para>Static pointcuts are based on method and target class, and
cannot take into account the method's arguments. Static pointcuts are
sufficient - <emphasis>and best</emphasis> - for most usages. It's
possible for Spring to evaluate a static pointcut only once, when a
method is first invoked: after that, there is no need to evaluate the
pointcut again with each method invocation.</para>
<para>Let's consider some static pointcut implementations included
with Spring.</para>
<section xml:id="aop-api-pointcuts-regex">
<title>Regular expression pointcuts</title>
<para>One obvious way to specify static pointcuts is regular
expressions. Several AOP frameworks besides Spring make this
possible.
<literal>org.springframework.aop.support.JdkRegexpMethodPointcut</literal>
is a generic regular expression pointcut, using the regular
expression support in JDK 1.4+.</para>
<para>Using the <literal>JdkRegexpMethodPointcut</literal> class,
you can provide a list of pattern Strings. If any of these is a
match, the pointcut will evaluate to true. (So the result is
effectively the union of these pointcuts.)</para>
<para>The usage is shown below:</para>
<para><programlisting language="xml"><bean id="settersAndAbsquatulatePointcut"
class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="patterns">
<list>
<value>.*set.*</value>
<value>.*absquatulate</value>
</list>
</property>
</bean></programlisting></para>
<para>Spring provides a convenience class,
<literal>RegexpMethodPointcutAdvisor</literal>, that allows us to
also reference an Advice (remember that an Advice can be an
interceptor, before advice, throws advice etc.). Behind the scenes,
Spring will use a <literal>JdkRegexpMethodPointcut</literal>. Using
<literal>RegexpMethodPointcutAdvisor</literal> simplifies wiring, as
the one bean encapsulates both pointcut and advice, as shown
below:</para>
<para><programlisting language="xml"><bean id="settersAndAbsquatulateAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref bean="beanNameOfAopAllianceInterceptor"/>
</property>
<property name="patterns">
<list>
<value>.*set.*</value>
<value>.*absquatulate</value>
</list>
</property>
</bean></programlisting></para>
<para><emphasis>RegexpMethodPointcutAdvisor</emphasis> can be used
with any Advice type.</para>
</section>
<section xml:id="aop-api-pointcuts-attribute-driven">
<title>Attribute-driven pointcuts</title>
<para>An important type of static pointcut is a
<emphasis>metadata-driven</emphasis> pointcut. This uses the values
of metadata attributes: typically, source-level metadata.</para>
</section>
</section>
<section xml:id="aop-api-pointcuts-dynamic">
<title>Dynamic pointcuts</title>
<para>Dynamic pointcuts are costlier to evaluate than static
pointcuts. They take into account method
<emphasis>arguments</emphasis>, as well as static information. This
means that they must be evaluated with every method invocation; the
result cannot be cached, as arguments will vary.</para>
<para>The main example is the <literal>control flow</literal>
pointcut.</para>
<section xml:id="aop-api-pointcuts-cflow">
<title>Control flow pointcuts</title>
<para>Spring control flow pointcuts are conceptually similar to
AspectJ <emphasis>cflow</emphasis> pointcuts, although less
powerful. (There is currently no way to specify that a pointcut
executes below a join point matched by another pointcut.) A control
flow pointcut matches the current call stack. For example, it might
fire if the join point was invoked by a method in the
<literal>com.mycompany.web</literal> package, or by the
<literal>SomeCaller</literal> class. Control flow pointcuts are
specified using the
<literal>org.springframework.aop.support.ControlFlowPointcut
</literal>class.<note>
<para>Control flow pointcuts are significantly more expensive to
evaluate at runtime than even other dynamic pointcuts. In Java
1.4, the cost is about 5 times that of other dynamic
pointcuts.</para>
</note></para>
</section>
</section>
</section>
<section xml:id="aop-api-pointcuts-superclasses">
<title>Pointcut superclasses</title>
<para>Spring provides useful pointcut superclasses to help you to
implement your own pointcuts.</para>
<para>Because static pointcuts are most useful, you'll probably subclass
StaticMethodMatcherPointcut, as shown below. This requires implementing
just one abstract method (although it's possible to override other
methods to customize behavior):</para>
<para><programlisting language="java">class TestStaticPointcut extends StaticMethodMatcherPointcut {
public boolean matches(Method m, Class targetClass) {
// return true if custom criteria match
}
}</programlisting>There are also superclasses for dynamic pointcuts.</para>
<para>You can use custom pointcuts with any advice type in Spring 1.0
RC2 and above.</para>
</section>
<section xml:id="aop-api-pointcuts-custom">
<title>Custom pointcuts</title>
<para>Because pointcuts in Spring AOP are Java classes, rather than
language features (as in AspectJ) it's possible to declare custom
pointcuts, whether static or dynamic. Custom pointcuts in Spring can be
arbitrarily complex. However, using the AspectJ pointcut expression
language is recommended if possible.</para>
<note>
<para>Later versions of Spring may offer support for "semantic
pointcuts" as offered by JAC: for example, "all methods that change
instance variables in the target object."</para>
</note>
</section>
</section>
<section xml:id="aop-api-advice">
<title>Advice API in Spring</title>
<para>Let's now look at how Spring AOP handles advice.</para>
<section xml:id="aop-api-advice-lifecycle">
<title>Advice lifecycles</title>
<para>Each advice is a Spring bean. An advice instance can be shared
across all advised objects, or unique to each advised object. This
corresponds to <emphasis>per-class</emphasis> or
<emphasis>per-instance</emphasis> advice.</para>
<para>Per-class advice is used most often. It is appropriate for generic
advice such as transaction advisors. These do not depend on the state of
the proxied object or add new state; they merely act on the method and
arguments.</para>
<para>Per-instance advice is appropriate for introductions, to support
mixins. In this case, the advice adds state to the proxied
object.</para>
<para>It's possible to use a mix of shared and per-instance advice in
the same AOP proxy.</para>
</section>
<section xml:id="aop-api-advice-types">
<title>Advice types in Spring</title>
<para>Spring provides several advice types out of the box, and is
extensible to support arbitrary advice types. Let us look at the basic
concepts and standard advice types.</para>
<section xml:id="aop-api-advice-around">
<title>Interception around advice</title>
<para>The most fundamental advice type in Spring is
<emphasis>interception around advice</emphasis>.</para>
<para>Spring is compliant with the AOP Alliance interface for around
advice using method interception. MethodInterceptors implementing
around advice should implement the following interface:</para>
<programlisting language="java">public interface MethodInterceptor extends Interceptor {
Object invoke(MethodInvocation invocation) throws Throwable;
}</programlisting>
<para>The <classname>MethodInvocation</classname> argument to the
<methodname>invoke()</methodname> method exposes the method being
invoked; the target join point; the AOP proxy; and the arguments to
the method. The <methodname>invoke()</methodname> method should return
the invocation's result: the return value of the join point.</para>
<para>A simple <classname>MethodInterceptor</classname> implementation
looks as follows:</para>
<programlisting language="java">public class DebugInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("Before: invocation=[" + invocation + "]");
Object rval = invocation.proceed();
System.out.println("Invocation returned");
return rval;
}
}</programlisting>
<para>Note the call to the MethodInvocation's
<methodname>proceed()</methodname> method. This proceeds down the
interceptor chain towards the join point. Most interceptors will
invoke this method, and return its return value. However, a
MethodInterceptor, like any around advice, can return a different
value or throw an exception rather than invoke the proceed method.
However, you don't want to do this without good reason!</para>
<note>
<para>MethodInterceptors offer interoperability with other AOP
Alliance-compliant AOP implementations. The other advice types
discussed in the remainder of this section implement common AOP
concepts, but in a Spring-specific way. While there is an advantage
in using the most specific advice type, stick with MethodInterceptor
around advice if you are likely to want to run the aspect in another
AOP framework. Note that pointcuts are not currently interoperable
between frameworks, and the AOP Alliance does not currently define
pointcut interfaces.</para>
</note>
</section>
<section xml:id="aop-api-advice-before">
<title>Before advice</title>
<para>A simpler advice type is a <emphasis role="bold">before
advice</emphasis>. This does not need a
<literal>MethodInvocation</literal> object, since it will only be
called before entering the method.</para>
<para>The main advantage of a before advice is that there is no need
to invoke the <literal>proceed() </literal>method, and therefore no
possibility of inadvertently failing to proceed down the interceptor
chain.</para>
<para>The <literal>MethodBeforeAdvice</literal> interface is shown
below. (Spring's API design would allow for field before advice,
although the usual objects apply to field interception and it's
unlikely that Spring will ever implement it).</para>
<programlisting language="java">public interface MethodBeforeAdvice extends BeforeAdvice {
void before(Method m, Object[] args, Object target) throws Throwable;
}</programlisting>
<para>Note the return type is <literal>void</literal>. Before advice
can insert custom behavior before the join point executes, but cannot
change the return value. If a before advice throws an exception, this
will abort further execution of the interceptor chain. The exception
will propagate back up the interceptor chain. If it is unchecked, or
on the signature of the invoked method, it will be passed directly to
the client; otherwise it will be wrapped in an unchecked exception by
the AOP proxy.</para>
<para>An example of a before advice in Spring, which counts all method
invocations:</para>
<programlisting language="java">public class CountingBeforeAdvice implements MethodBeforeAdvice {
private int count;
public void before(Method m, Object[] args, Object target) throws Throwable {
++count;
}
public int getCount() {
return count;
}
}</programlisting>
<tip>
<para>Before advice can be used with any pointcut.</para>
</tip>
</section>
<section xml:id="aop-api-advice-throws">
<title>Throws advice</title>
<para><emphasis role="bold">Throws advice</emphasis> is invoked after
the return of the join point if the join point threw an exception.
Spring offers typed throws advice. Note that this means that the
<literal>org.springframework.aop.ThrowsAdvice</literal> interface does
not contain any methods: It is a tag interface identifying that the
given object implements one or more typed throws advice methods. These
should be in the form of:</para>
<programlisting language="java">afterThrowing([Method, args, target], subclassOfThrowable) </programlisting>
<para>Only the last argument is required. The method signatures may
have either one or four arguments, depending on whether the advice
method is interested in the method and arguments. The following
classes are examples of throws advice.</para>
<para>The advice below is invoked if a
<exceptionname>RemoteException</exceptionname> is thrown (including
subclasses):</para>
<programlisting language="java">public class RemoteThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(RemoteException ex) throws Throwable {
<lineannotation>// Do something with remote exception</lineannotation>
}
}</programlisting>
<para>The following advice is invoked if a
<exceptionname>ServletException</exceptionname> is thrown. Unlike the
above advice, it declares 4 arguments, so that it has access to the
invoked method, method arguments and target object:</para>
<programlisting language="java">public class ServletThrowsAdviceWithArguments implements ThrowsAdvice {
public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) {
<lineannotation>// Do something with all arguments</lineannotation>
}
}</programlisting>
<para>The final example illustrates how these two methods could be
used in a single class, which handles both
<literal>RemoteException</literal> and
<literal>ServletException</literal>. Any number of throws advice
methods can be combined in a single class.</para>
<programlisting language="java">public static class CombinedThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(RemoteException ex) throws Throwable {
// Do something with remote exception
}
public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) {
// Do something with all arguments
}
}</programlisting>
<para><emphasis>Note:</emphasis> If a throws-advice method throws an
exception itself, it will override the original exception (i.e. change
the exception thrown to the user). The overriding exception will
typically be a RuntimeException; this is compatible with any method
signature. However, if a throws-advice method throws a checked
exception, it will have to match the declared exceptions of the target
method and is hence to some degree coupled to specific target method
signatures. <emphasis>Do not throw an undeclared checked exception
that is incompatible with the target method's
signature!</emphasis></para>
<tip>
<para>Throws advice can be used with any pointcut.</para>
</tip>
</section>
<section xml:id="aop-api-advice-after-returning">
<title>After Returning advice</title>
<para>An after returning advice in Spring must implement the
<emphasis>org.springframework.aop.AfterReturningAdvice</emphasis>
interface, shown below:</para>
<programlisting language="java">public interface AfterReturningAdvice extends Advice {
void afterReturning(Object returnValue, Method m, Object[] args, Object target)
throws Throwable;
}</programlisting>
<para>An after returning advice has access to the return value (which
it cannot modify), invoked method, methods arguments and
target.</para>
<para>The following after returning advice counts all successful
method invocations that have not thrown exceptions:</para>
<programlisting language="java">public class CountingAfterReturningAdvice implements AfterReturningAdvice {
private int count;
public void afterReturning(Object returnValue, Method m, Object[] args, Object target)
throws Throwable {
++count;
}
public int getCount() {
return count;
}
}</programlisting>
<para>This advice doesn't change the execution path. If it throws an
exception, this will be thrown up the interceptor chain instead of the
return value.</para>
<tip>
<para>After returning advice can be used with any pointcut.</para>
</tip>
</section>
<section xml:id="aop-api-advice-introduction">
<title>Introduction advice</title>
<para>Spring treats introduction advice as a special kind of
interception advice.</para>
<para>Introduction requires an <literal>IntroductionAdvisor</literal>,
and an <literal>IntroductionInterceptor</literal>, implementing the
following interface:</para>
<programlisting language="java">public interface IntroductionInterceptor extends MethodInterceptor {
boolean implementsInterface(Class intf);
}</programlisting>
<para>The <literal>invoke() </literal>method inherited from the AOP
Alliance <literal>MethodInterceptor</literal> interface must implement
the introduction: that is, if the invoked method is on an introduced
interface, the introduction interceptor is responsible for handling
the method call - it cannot invoke
<literal>proceed()</literal>.</para>
<para>Introduction advice cannot be used with any pointcut, as it
applies only at class, rather than method, level. You can only use
introduction advice with the <literal>IntroductionAdvisor</literal>,
which has the following methods:</para>
<programlisting language="java">public interface IntroductionAdvisor extends Advisor, IntroductionInfo {
ClassFilter getClassFilter();
void validateInterfaces() throws IllegalArgumentException;
}
public interface IntroductionInfo {
Class[] getInterfaces();
}</programlisting>
<para>There is no <interfacename>MethodMatcher</interfacename>, and
hence no <interfacename>Pointcut</interfacename>, associated with
introduction advice. Only class filtering is logical.</para>
<para>The <literal>getInterfaces()</literal> method returns the
interfaces introduced by this advisor.</para>
<para>The <literal>validateInterfaces()</literal> method is used internally to
see whether or not the introduced interfaces can be implemented by the configured
<literal>IntroductionInterceptor</literal>.</para>
<para>Let's look at a simple example from the Spring test suite. Let's
suppose we want to introduce the following interface to one or more
objects:</para>
<para>
<programlisting language="java">public interface Lockable {
void lock();
void unlock();
boolean locked();
}</programlisting>
</para>
<para>This illustrates a <emphasis role="bold">mixin</emphasis>. We
want to be able to cast advised objects to Lockable, whatever their
type, and call lock and unlock methods. If we call the lock() method,
we want all setter methods to throw a
<literal>LockedException</literal>. Thus we can add an aspect that
provides the ability to make objects immutable, without them having
any knowledge of it: a good example of AOP.</para>
<para>Firstly, we'll need an
<literal>IntroductionInterceptor</literal> that does the heavy
lifting. In this case, we extend the
<literal>org.springframework.aop.support.DelegatingIntroductionInterceptor</literal>
convenience class. We could implement IntroductionInterceptor
directly, but using
<literal>DelegatingIntroductionInterceptor</literal> is best for most
cases.</para>
<para>The <literal>DelegatingIntroductionInterceptor</literal> is
designed to delegate an introduction to an actual implementation of
the introduced interface(s), concealing the use of interception to do
so. The delegate can be set to any object using a constructor
argument; the default delegate (when the no-arg constructor is used)
is this. Thus in the example below, the delegate is the
<literal>LockMixin</literal> subclass of
<literal>DelegatingIntroductionInterceptor</literal>. Given a delegate
(by default itself), a
<literal>DelegatingIntroductionInterceptor</literal> instance looks
for all interfaces implemented by the delegate (other than
IntroductionInterceptor), and will support introductions against any
of them. It's possible for subclasses such as
<literal>LockMixin</literal> to call the
<literal>suppressInterface(Class intf) </literal>method to suppress
interfaces that should not be exposed. However, no matter how many
interfaces an <literal>IntroductionInterceptor</literal> is prepared
to support, the <literal>IntroductionAdvisor</literal> used will
control which interfaces are actually exposed. An introduced interface
will conceal any implementation of the same interface by the
target.</para>
<para>Thus LockMixin subclasses
<literal>DelegatingIntroductionInterceptor</literal> and implements
Lockable itself. The superclass automatically picks up that Lockable
can be supported for introduction, so we don't need to specify that.
We could introduce any number of interfaces in this way.</para>
<para>Note the use of the <literal>locked</literal> instance variable.
This effectively adds additional state to that held in the target
object.</para>
<para>
<programlisting language="java">public class LockMixin extends DelegatingIntroductionInterceptor
implements Lockable {
private boolean locked;
public void lock() {
this.locked = true;
}
public void unlock() {
this.locked = false;
}
public boolean locked() {
return this.locked;
}
public Object invoke(MethodInvocation invocation) throws Throwable {
if (locked() && invocation.getMethod().getName().indexOf("set") == 0)
throw new LockedException();
return super.invoke(invocation);
}
}</programlisting>
</para>
<para>Often it isn't necessary to override the <literal>invoke()
</literal>method: the
<literal>DelegatingIntroductionInterceptor</literal> implementation -
which calls the delegate method if the method is introduced, otherwise
proceeds towards the join point - is usually sufficient. In the
present case, we need to add a check: no setter method can be invoked
if in locked mode.</para>
<para>The introduction advisor required is simple. All it needs to do
is hold a distinct <literal>LockMixin</literal> instance, and specify
the introduced interfaces - in this case, just
<literal>Lockable</literal>. A more complex example might take a
reference to the introduction interceptor (which would be defined as a
prototype): in this case, there's no configuration relevant for a
<literal>LockMixin</literal>, so we simply create it using
<literal>new</literal>.</para>
<para>
<programlisting language="java">public class LockMixinAdvisor extends DefaultIntroductionAdvisor {
public LockMixinAdvisor() {
super(new LockMixin(), Lockable.class);
}
}</programlisting>
</para>
<para>We can apply this advisor very simply: it requires no
configuration. (However, it <emphasis>is</emphasis> necessary: It's
impossible to use an <literal>IntroductionInterceptor</literal>
without an <emphasis>IntroductionAdvisor</emphasis>.) As usual with
introductions, the advisor must be per-instance, as it is stateful. We
need a different instance of <literal>LockMixinAdvisor</literal>, and
hence <literal>LockMixin</literal>, for each advised object. The
advisor comprises part of the advised object's state.</para>
<para>We can apply this advisor programmatically, using the
<literal>Advised.addAdvisor() </literal>method, or (the recommended
way) in XML configuration, like any other advisor. All proxy creation
choices discussed below, including "auto proxy creators," correctly
handle introductions and stateful mixins.</para>
</section>
</section>
</section>
<section xml:id="aop-api-advisor">
<title>Advisor API in Spring</title>
<para>In Spring, an Advisor is an aspect that contains just a single
advice object associated with a pointcut expression.</para>
<para>Apart from the special case of introductions, any advisor can be
used with any advice.
<literal>org.springframework.aop.support.DefaultPointcutAdvisor</literal>
is the most commonly used advisor class. For example, it can be used with
a <literal>MethodInterceptor</literal>, <literal>BeforeAdvice</literal> or
<literal>ThrowsAdvice</literal>.</para>
<para>It is possible to mix advisor and advice types in Spring in the same
AOP proxy. For example, you could use a interception around advice, throws
advice and before advice in one proxy configuration: Spring will
automatically create the necessary interceptor chain.</para>
</section>
<section xml:id="aop-pfb">
<title>Using the ProxyFactoryBean to create AOP proxies</title>
<para>If you're using the Spring IoC container (an ApplicationContext or
BeanFactory) for your business objects - and you should be! - you will
want to use one of Spring's AOP FactoryBeans. (Remember that a factory
bean introduces a layer of indirection, enabling it to create objects of a
different type.)</para>
<note>
<para>The Spring 2.0 AOP support also uses factory beans under the
covers.</para>
</note>
<para>The basic way to create an AOP proxy in Spring is to use the
<emphasis>org.springframework.aop.framework.ProxyFactoryBean</emphasis>.
This gives complete control over the pointcuts and advice that will apply,
and their ordering. However, there are simpler options that are preferable
if you don't need such control.</para>
<section xml:id="aop-pfb-1">
<title>Basics</title>
<para>The <literal>ProxyFactoryBean</literal>, like other Spring
<literal>FactoryBean</literal> implementations, introduces a level of
indirection. If you define a <literal>ProxyFactoryBean</literal> with
name <literal>foo</literal>, what objects referencing
<literal>foo</literal> see is not the
<literal>ProxyFactoryBean</literal> instance itself, but an object
created by the <literal>ProxyFactoryBean</literal>'s implementation of
the <literal>getObject() </literal>method. This method will create an
AOP proxy wrapping a target object.</para>
<para>One of the most important benefits of using a
<literal>ProxyFactoryBean</literal> or another IoC-aware class to create
AOP proxies, is that it means that advices and pointcuts can also be
managed by IoC. This is a powerful feature, enabling certain approaches
that are hard to achieve with other AOP frameworks. For example, an
advice may itself reference application objects (besides the target,
which should be available in any AOP framework), benefiting from all the
pluggability provided by Dependency Injection.</para>
</section>
<section xml:id="aop-pfb-2">
<title>JavaBean properties</title>
<para>In common with most <interfacename>FactoryBean</interfacename>
implementations provided with Spring, the
<classname>ProxyFactoryBean</classname> class is itself a JavaBean. Its
properties are used to:</para>
<itemizedlist>
<listitem>
<para>Specify the target you want to proxy.</para>
</listitem>
<listitem>
<para>Specify whether to use CGLIB (see below and also
<xref linkend="aop-pfb-proxy-types" />).</para>
</listitem>
</itemizedlist>
<para>Some key properties are inherited from
<classname>org.springframework.aop.framework.ProxyConfig</classname>
(the superclass for all AOP proxy factories in Spring). These key
properties include:</para>
<itemizedlist>
<listitem>
<para><literal>proxyTargetClass</literal>: <literal>true</literal>
if the target class is to be proxied, rather than the target class'
interfaces. If this property value is set to
<literal>true</literal>, then CGLIB proxies will be created (but see
also <xref linkend="aop-pfb-proxy-types" />).</para>
</listitem>
<listitem>
<para><literal>optimize</literal>: controls whether or not
aggressive optimizations are applied to proxies <emphasis>created
via CGLIB</emphasis>. One should not blithely use this setting
unless one fully understands how the relevant AOP proxy handles
optimization. This is currently used only for CGLIB proxies; it has
no effect with JDK dynamic proxies.</para>
</listitem>
<listitem>
<para><literal>frozen</literal>: if a proxy configuration is
<literal>frozen</literal>, then changes to the configuration are no
longer allowed. This is useful both as a slight optimization and for
those cases when you don't want callers to be able to manipulate the
proxy (via the <interfacename>Advised</interfacename> interface)
after the proxy has been created. The default value of this property
is <literal>false</literal>, so changes such as adding additional
advice are allowed.</para>
</listitem>
<listitem>
<para><literal>exposeProxy</literal>: determines whether or not the
current proxy should be exposed in a
<classname>ThreadLocal</classname> so that it can be accessed by the
target. If a target needs to obtain the proxy and the
<literal>exposeProxy</literal> property is set to
<literal>true</literal>, the target can use the
<methodname>AopContext.currentProxy()</methodname> method.</para>
</listitem>
</itemizedlist>
<para>Other properties specific to
<classname>ProxyFactoryBean</classname> include:</para>
<itemizedlist>
<listitem>
<para><literal>proxyInterfaces</literal>: array of String interface
names. If this isn't supplied, a CGLIB proxy for the target class
will be used (but see also <xref linkend="aop-pfb-proxy-types" />).</para>
</listitem>
<listitem>
<para><literal>interceptorNames</literal>: String array of
<interfacename>Advisor</interfacename>, interceptor or other advice
names to apply. Ordering is significant, on a first come-first
served basis. That is to say that the first interceptor in the list
will be the first to be able to intercept the invocation.</para>
<para>The names are bean names in the current factory, including
bean names from ancestor factories. You can't mention bean
references here since doing so would result in the
<classname>ProxyFactoryBean</classname> ignoring the singleton
setting of the advice.</para>
<para>You can append an interceptor name with an asterisk
(<literal>*</literal>). This will result in the application of all
advisor beans with names starting with the part before the asterisk
to be applied. An example of using this feature can be found in
<xref linkend="aop-global-advisors" />.</para>
</listitem>
<listitem>
<para>singleton: whether or not the factory should return a single
object, no matter how often the <literal>getObject()</literal>
method is called. Several <interfacename>FactoryBean</interfacename>
implementations offer such a method. The default value is
<literal>true</literal>. If you want to use stateful advice - for
example, for stateful mixins - use prototype advices along with a
singleton value of <literal>false</literal>.</para>
</listitem>
</itemizedlist>
</section>
<section xml:id="aop-pfb-proxy-types">
<title>JDK- and CGLIB-based proxies</title>
<para>This section serves as the definitive documentation on how the
<classname>ProxyFactoryBean</classname> chooses to create one of either
a JDK- and CGLIB-based proxy for a particular target object (that is to
be proxied).</para>
<note>
<para>The behavior of the <classname>ProxyFactoryBean</classname> with
regard to creating JDK- or CGLIB-based proxies changed between
versions 1.2.x and 2.0 of Spring. The
<classname>ProxyFactoryBean</classname> now exhibits similar semantics
with regard to auto-detecting interfaces as those of the
<classname>TransactionProxyFactoryBean</classname> class.</para>
</note>
<para>If the class of a target object that is to be proxied (hereafter
simply referred to as the target class) doesn't implement any
interfaces, then a CGLIB-based proxy will be created. This is the
easiest scenario, because JDK proxies are interface based, and no
interfaces means JDK proxying isn't even possible. One simply plugs in
the target bean, and specifies the list of interceptors via the
<literal>interceptorNames</literal> property. Note that a CGLIB-based
proxy will be created even if the <literal>proxyTargetClass</literal>
property of the <classname>ProxyFactoryBean</classname> has been set to
<literal>false</literal>. (Obviously this makes no sense, and is best
removed from the bean definition because it is at best redundant, and at
worst confusing.)</para>
<para>If the target class implements one (or more) interfaces, then the
type of proxy that is created depends on the configuration of the
<classname>ProxyFactoryBean</classname>.</para>
<para>If the <literal>proxyTargetClass</literal> property of the
<classname>ProxyFactoryBean</classname> has been set to
<literal>true</literal>, then a CGLIB-based proxy will be created. This
makes sense, and is in keeping with the principle of least surprise.
Even if the <literal>proxyInterfaces</literal> property of the
<classname>ProxyFactoryBean</classname> has been set to one or more
fully qualified interface names, the fact that the
<literal>proxyTargetClass</literal> property is set to
<literal>true</literal> <emphasis>will</emphasis> cause CGLIB-based
proxying to be in effect.</para>
<para>If the <literal>proxyInterfaces</literal> property of the
<classname>ProxyFactoryBean</classname> has been set to one or more
fully qualified interface names, then a JDK-based proxy will be created.
The created proxy will implement all of the interfaces that were
specified in the <literal>proxyInterfaces</literal> property; if the
target class happens to implement a whole lot more interfaces than those