KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > aop > framework > Cglib2AopProxy


1 /*
2  * Copyright 2002-2007 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.springframework.aop.framework;
18
19 import java.io.Serializable JavaDoc;
20 import java.lang.reflect.Method JavaDoc;
21 import java.lang.reflect.Modifier JavaDoc;
22 import java.lang.reflect.UndeclaredThrowableException JavaDoc;
23 import java.util.HashMap JavaDoc;
24 import java.util.HashSet JavaDoc;
25 import java.util.List JavaDoc;
26 import java.util.Map JavaDoc;
27 import java.util.Set JavaDoc;
28
29 import net.sf.cglib.core.CodeGenerationException;
30 import net.sf.cglib.proxy.Callback;
31 import net.sf.cglib.proxy.CallbackFilter;
32 import net.sf.cglib.proxy.Dispatcher;
33 import net.sf.cglib.proxy.Enhancer;
34 import net.sf.cglib.proxy.Factory;
35 import net.sf.cglib.proxy.MethodInterceptor;
36 import net.sf.cglib.proxy.MethodProxy;
37 import net.sf.cglib.proxy.NoOp;
38 import net.sf.cglib.transform.impl.UndeclaredThrowableStrategy;
39 import org.aopalliance.aop.Advice;
40 import org.aopalliance.intercept.MethodInvocation;
41 import org.apache.commons.logging.Log;
42 import org.apache.commons.logging.LogFactory;
43
44 import org.springframework.aop.Advisor;
45 import org.springframework.aop.PointcutAdvisor;
46 import org.springframework.aop.support.AopUtils;
47 import org.springframework.util.Assert;
48 import org.springframework.util.ObjectUtils;
49
50 /**
51  * CGLIB2-based {@link AopProxy} implementation for the Spring AOP framework.
52  *
53  * <p><i>Requires CGLIB 2.1+ on the classpath.</i>.
54  * As of Spring 2.0, earlier CGLIB versions are not supported anymore.
55  *
56  * <p>Objects of this type should be obtained through proxy factories,
57  * configured by an {@link AdvisedSupport} object. This class is internal
58  * to Spring's AOP framework and need not be used directly by client code.
59  *
60  * <p>{@link DefaultAopProxyFactory} will automatically create CGLIB2-based
61  * proxies if necessary, for example in case of proxying a target class
62  * (see the {@link DefaultAopProxyFactory attendant javadoc} for details).
63  *
64  * <p>Proxies created using this class are thread-safe if the underlying
65  * (target) class is thread-safe.
66  *
67  * @author Rod Johnson
68  * @author Rob Harrop
69  * @author Juergen Hoeller
70  * @see net.sf.cglib.proxy.Enhancer
71  * @see AdvisedSupport#setProxyTargetClass
72  * @see DefaultAopProxyFactory
73  */

74 final class Cglib2AopProxy implements AopProxy, Serializable JavaDoc {
75
76     // Constants for CGLIB callback array indices
77
private static final int AOP_PROXY = 0;
78     private static final int INVOKE_TARGET = 1;
79     private static final int NO_OVERRIDE = 2;
80     private static final int DISPATCH_TARGET = 3;
81     private static final int DISPATCH_ADVISED = 4;
82     private static final int INVOKE_EQUALS = 5;
83     private static final int INVOKE_HASHCODE = 6;
84
85
86     /** Logger available to subclasses; static to optimize serialization */
87     protected final static Log logger = LogFactory.getLog(Cglib2AopProxy.class);
88
89     /** Keeps track of the Classes that we have validated for final methods */
90     private static final Set JavaDoc validatedClasses = new HashSet JavaDoc();
91
92
93     /** The configuration used to configure this proxy */
94     protected final AdvisedSupport advised;
95
96     private Object JavaDoc[] constructorArgs;
97
98     private Class JavaDoc[] constructorArgTypes;
99
100     /** Dispatcher used for methods on Advised */
101     private final transient AdvisedDispatcher advisedDispatcher;
102
103     private transient Map JavaDoc fixedInterceptorMap;
104
105     private transient int fixedInterceptorOffset;
106
107
108     /**
109      * Create a new Cglib2AopProxy for the given AOP configuration.
110      * @param config the AOP configuration as AdvisedSupport object
111      * @throws AopConfigException if the config is invalid. We try to throw an informative
112      * exception in this case, rather than let a mysterious failure happen later.
113      */

114     public Cglib2AopProxy(AdvisedSupport config) throws AopConfigException {
115         Assert.notNull(config, "AdvisedSupport must not be null");
116         if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
117             throw new AopConfigException("No advisors and no TargetSource specified");
118         }
119         this.advised = config;
120         this.advisedDispatcher = new AdvisedDispatcher(this.advised);
121     }
122
123     /**
124      * Set constructor arguments to use for creating the proxy.
125      * @param constructorArgs the constructor argument values
126      * @param constructorArgTypes the constructor argument types
127      */

128     public void setConstructorArguments(Object JavaDoc[] constructorArgs, Class JavaDoc[] constructorArgTypes) {
129         if (constructorArgs == null || constructorArgTypes == null) {
130             throw new IllegalArgumentException JavaDoc("Both 'constructorArgs' and 'constructorArgTypes' need to be specified");
131         }
132         if (constructorArgs.length != constructorArgTypes.length) {
133             throw new IllegalArgumentException JavaDoc("Number of 'constructorArgs' (" + constructorArgs.length +
134                     ") must match number of 'constructorArgTypes' (" + constructorArgTypes.length + ")");
135         }
136         this.constructorArgs = constructorArgs;
137         this.constructorArgTypes = constructorArgTypes;
138     }
139
140
141     public Object JavaDoc getProxy() {
142         return getProxy(null);
143     }
144
145     public Object JavaDoc getProxy(ClassLoader JavaDoc classLoader) {
146         if (logger.isDebugEnabled()) {
147             logger.debug("Creating CGLIB2 proxy: target source is " + this.advised.getTargetSource());
148         }
149
150         Enhancer enhancer = createEnhancer();
151         try {
152             // Create proxy in specific ClassLoader, if given.
153
if (classLoader != null) {
154                 enhancer.setClassLoader(classLoader);
155             }
156
157             Class JavaDoc rootClass = this.advised.getTargetClass();
158             Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
159             Class JavaDoc proxySuperClass = rootClass;
160
161             if (AopUtils.isCglibProxyClass(rootClass)) {
162                 proxySuperClass = rootClass.getSuperclass();
163                 Class JavaDoc[] additionalInterfaces = rootClass.getInterfaces();
164                 for (int i = 0; i < additionalInterfaces.length; i++) {
165                     Class JavaDoc additionalInterface = additionalInterfaces[i];
166                     this.advised.addInterface(additionalInterface);
167                 }
168             }
169
170             // Validate the class, writing log messages as necessary.
171
validateClassIfNecessary(proxySuperClass);
172
173             enhancer.setSuperclass(proxySuperClass);
174             enhancer.setStrategy(new UndeclaredThrowableStrategy(UndeclaredThrowableException JavaDoc.class));
175             enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
176             enhancer.setInterceptDuringConstruction(false);
177
178             Callback[] callbacks = getCallbacks(rootClass);
179             enhancer.setCallbacks(callbacks);
180             enhancer.setCallbackFilter(
181                     new ProxyCallbackFilter(this.advised, this.fixedInterceptorMap, this.fixedInterceptorOffset));
182
183             Class JavaDoc[] types = new Class JavaDoc[callbacks.length];
184             for (int x = 0; x < types.length; x++) {
185                 types[x] = callbacks[x].getClass();
186             }
187             enhancer.setCallbackTypes(types);
188
189             // Generate the proxy class and create a proxy instance.
190
Object JavaDoc proxy;
191             if (this.constructorArgs != null) {
192                 proxy = enhancer.create(this.constructorArgTypes, this.constructorArgs);
193             }
194             else {
195                 proxy = enhancer.create();
196             }
197
198             return proxy;
199         }
200         catch (CodeGenerationException ex) {
201             throw new AopConfigException("Couldn't generate CGLIB subclass of class [" +
202                     this.advised.getTargetClass() + "]: " +
203                     "Common causes of this problem include using a final class or a non-visible class",
204                     ex);
205         }
206         catch (IllegalArgumentException JavaDoc ex) {
207             throw new AopConfigException("Couldn't generate CGLIB subclass of class [" +
208                     this.advised.getTargetClass() + "]: " +
209                     "Common causes of this problem include using a final class or a non-visible class",
210                     ex);
211         }
212         catch (Exception JavaDoc ex) {
213             // TargetSource.getTarget() failed
214
throw new AopConfigException("Unexpected AOP exception", ex);
215         }
216     }
217
218     /**
219      * Creates the CGLIB {@link Enhancer}. Subclasses may wish to override this to return a custom
220      * {@link Enhancer} implementation.
221      */

222     protected Enhancer createEnhancer() {
223         return new Enhancer();
224     }
225
226     /**
227      * Checks to see whether the supplied <code>Class</code> has already been validated and
228      * validates it if not.
229      */

230     private void validateClassIfNecessary(Class JavaDoc proxySuperClass) {
231         if (logger.isWarnEnabled()) {
232             synchronized (validatedClasses) {
233                 if (!validatedClasses.contains(proxySuperClass)) {
234                     doValidateClass(proxySuperClass);
235                     validatedClasses.add(proxySuperClass);
236                 }
237             }
238         }
239     }
240
241     /**
242      * Checks for final methods on the <code>Class</code> and writes warnings to the log
243      * for each one found.
244      */

245     private void doValidateClass(Class JavaDoc proxySuperClass) {
246         Method JavaDoc[] methods = proxySuperClass.getMethods();
247         for (int i = 0; i < methods.length; i++) {
248             Method JavaDoc method = methods[i];
249             if (!Object JavaDoc.class.equals(method.getDeclaringClass()) && Modifier.isFinal(method.getModifiers())) {
250                 logger.warn("Unable to proxy method [" + method + "] because it is final: " +
251                         "All calls to this method via a proxy will be routed directly to the proxy.");
252             }
253         }
254     }
255
256     private Callback[] getCallbacks(Class JavaDoc rootClass) throws Exception JavaDoc {
257         // Parameters used for optimisation choices...
258
boolean exposeProxy = this.advised.isExposeProxy();
259         boolean isFrozen = this.advised.isFrozen();
260         boolean isStatic = this.advised.getTargetSource().isStatic();
261
262         // Choose an "aop" interceptor (used for AOP calls).
263
Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
264
265         // Choose a "straight to target" interceptor. (used for calls that are
266
// unadvised but can return this). May be required to expose the proxy.
267
Callback targetInterceptor = null;
268
269         if (exposeProxy) {
270             targetInterceptor = isStatic ?
271                     (Callback) new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) :
272                     (Callback) new DynamicUnadvisedExposedInterceptor(this.advised);
273         }
274         else {
275             targetInterceptor = isStatic ?
276                     (Callback) new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :
277                     (Callback) new DynamicUnadvisedInterceptor(this.advised);
278         }
279
280         // Choose a "direct to target" dispatcher (used for
281
// unadvised calls to static targets that cannot return this).
282
Callback targetDispatcher = isStatic ?
283                 (Callback) new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp();
284
285         Callback[] mainCallbacks = new Callback[]{
286             aopInterceptor, // for normal advice
287
targetInterceptor, // invoke target without considering advice, if optimized
288
new SerializableNoOp(), // no override for methods mapped to this
289
targetDispatcher, this.advisedDispatcher,
290             new EqualsInterceptor(this.advised),
291             new HashCodeInterceptor(this.advised)
292         };
293
294         Callback[] callbacks;
295
296         // If the target is a static one and the advice chain is frozen,
297
// then we can make some optimisations by sending the AOP calls
298
// direct to the target using the fixed chain for that method.
299
if (isStatic && isFrozen) {
300             Method JavaDoc[] methods = rootClass.getMethods();
301             Callback[] fixedCallbacks = new Callback[methods.length];
302             this.fixedInterceptorMap = new HashMap JavaDoc(methods.length);
303
304             // TODO: small memory optimisation here (can skip creation for
305
// methods with no advice)
306
for (int x = 0; x < methods.length; x++) {
307                 List JavaDoc chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(methods[x], rootClass);
308                 fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(
309                         chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass());
310                 this.fixedInterceptorMap.put(methods[x].toString(), new Integer JavaDoc(x));
311             }
312
313             // Now copy both the callbacks from mainCallbacks
314
// and fixedCallbacks into the callbacks array.
315
callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length];
316
317             for (int x = 0; x < mainCallbacks.length; x++) {
318                 callbacks[x] = mainCallbacks[x];
319             }
320
321             for (int x = 0; x < fixedCallbacks.length; x++) {
322                 callbacks[x + mainCallbacks.length] = fixedCallbacks[x];
323             }
324
325             this.fixedInterceptorOffset = mainCallbacks.length;
326         }
327         else {
328             callbacks = mainCallbacks;
329         }
330         return callbacks;
331     }
332
333     /**
334      * Wrap a return of this if necessary to be the proxy
335      */

336     private static Object JavaDoc massageReturnTypeIfNecessary(Object JavaDoc proxy, Object JavaDoc target, Object JavaDoc retVal) {
337         // Massage return value if necessary
338
if (retVal != null && retVal == target) {
339             // Special case: it returned "this"
340
// Note that we can't help if the target sets a reference
341
// to itself in another returned object.
342
retVal = proxy;
343         }
344         return retVal;
345     }
346
347
348     public boolean equals(Object JavaDoc other) {
349         return (this == other || (other instanceof Cglib2AopProxy &&
350                 AopProxyUtils.equalsInProxy(this.advised, ((Cglib2AopProxy) other).advised)));
351     }
352
353     public int hashCode() {
354         return Cglib2AopProxy.class.hashCode() * 13 + this.advised.getTargetSource().hashCode();
355     }
356
357
358     /**
359      * Serializable replacement for CGLIB's NoOp interface.
360      * Public to allow use elsewhere in the framework.
361      */

362     public static class SerializableNoOp implements NoOp, Serializable JavaDoc {
363     }
364
365
366     /**
367      * Method interceptor used for static targets with no advice chain. The call
368      * is passed directly back to the target. Used when the proxy needs to be
369      * exposed and it can't be determined that the method won't return
370      * <code>this</code>.
371      */

372     private static class StaticUnadvisedInterceptor implements MethodInterceptor, Serializable JavaDoc {
373
374         private final Object JavaDoc target;
375
376         public StaticUnadvisedInterceptor(Object JavaDoc target) {
377             this.target = target;
378         }
379
380         public Object JavaDoc intercept(Object JavaDoc proxy, Method JavaDoc method, Object JavaDoc[] args, MethodProxy methodProxy) throws Throwable JavaDoc {
381             Object JavaDoc retVal = methodProxy.invoke(this.target, args);
382             return massageReturnTypeIfNecessary(proxy, this.target, retVal);
383         }
384     }
385
386
387     /**
388      * Method interceptor used for static targets with no advice chain, when the
389      * proxy is to be exposed.
390      */

391     private static class StaticUnadvisedExposedInterceptor implements MethodInterceptor, Serializable JavaDoc {
392
393         private final Object JavaDoc target;
394
395         public StaticUnadvisedExposedInterceptor(Object JavaDoc target) {
396             this.target = target;
397         }
398
399         public Object JavaDoc intercept(Object JavaDoc proxy, Method JavaDoc method, Object JavaDoc[] args, MethodProxy methodProxy) throws Throwable JavaDoc {
400             Object JavaDoc oldProxy = null;
401             try {
402                 oldProxy = AopContext.setCurrentProxy(proxy);
403                 Object JavaDoc retVal = methodProxy.invoke(this.target, args);
404                 return massageReturnTypeIfNecessary(proxy, this.target, retVal);
405             }
406             finally {
407                 AopContext.setCurrentProxy(oldProxy);
408             }
409         }
410     }
411
412
413     /**
414      * Interceptor used to invoke a dynamic target without creating a method
415      * invocation or evaluating an advice chain. (We know there was no advice
416      * for this method.)
417      */

418     private static class DynamicUnadvisedInterceptor implements MethodInterceptor, Serializable JavaDoc {
419
420         private final AdvisedSupport advised;
421
422         public DynamicUnadvisedInterceptor(AdvisedSupport advised) {
423             this.advised = advised;
424         }
425
426         public Object JavaDoc intercept(Object JavaDoc proxy, Method JavaDoc method, Object JavaDoc[] args, MethodProxy methodProxy) throws Throwable JavaDoc {
427             Object JavaDoc target = this.advised.getTargetSource().getTarget();
428             try {
429                 Object JavaDoc retVal = methodProxy.invoke(target, args);
430                 return massageReturnTypeIfNecessary(proxy, target, retVal);
431             }
432             finally {
433                 this.advised.getTargetSource().releaseTarget(target);
434             }
435         }
436     }
437
438
439     /**
440      * Interceptor for unadvised dynamic targets when the proxy needs exposing.
441      */

442     private static class DynamicUnadvisedExposedInterceptor implements MethodInterceptor, Serializable JavaDoc {
443
444         private final AdvisedSupport advised;
445
446         public DynamicUnadvisedExposedInterceptor(AdvisedSupport advised) {
447             this.advised = advised;
448         }
449
450         public Object JavaDoc intercept(Object JavaDoc proxy, Method JavaDoc method, Object JavaDoc[] args, MethodProxy methodProxy) throws Throwable JavaDoc {
451             Object JavaDoc oldProxy = null;
452             Object JavaDoc target = this.advised.getTargetSource().getTarget();
453             try {
454                 oldProxy = AopContext.setCurrentProxy(proxy);
455                 Object JavaDoc retVal = methodProxy.invoke(target, args);
456                 return massageReturnTypeIfNecessary(proxy, target, retVal);
457             }
458             finally {
459                 AopContext.setCurrentProxy(oldProxy);
460                 this.advised.getTargetSource().releaseTarget(target);
461             }
462         }
463     }
464
465
466     /**
467      * Dispatcher for a static target. Dispatcher is much faster than
468      * interceptor. This will be used whenever it can be determined that a
469      * method definitely does not return "this"
470      */

471     private static class StaticDispatcher implements Dispatcher, Serializable JavaDoc {
472
473         private Object JavaDoc target;
474
475         public StaticDispatcher(Object JavaDoc target) {
476             this.target = target;
477         }
478
479         public Object JavaDoc loadObject() {
480             return this.target;
481         }
482     }
483
484
485     /**
486      * Dispatcher for any methods declared on the Advised class.
487      */

488     private static class AdvisedDispatcher implements Dispatcher, Serializable JavaDoc {
489
490         private final AdvisedSupport advised;
491
492         public AdvisedDispatcher(AdvisedSupport advised) {
493             this.advised = advised;
494         }
495
496         public Object JavaDoc loadObject() throws Exception JavaDoc {
497             return this.advised;
498         }
499     }
500
501
502     /**
503      * Dispatcher for the <code>equals</code> method.
504      * Ensures that the method call is always handled by this class.
505      */

506     private static class EqualsInterceptor implements MethodInterceptor, Serializable JavaDoc {
507
508         private final AdvisedSupport advised;
509
510         public EqualsInterceptor(AdvisedSupport advised) {
511             this.advised = advised;
512         }
513
514         public Object JavaDoc intercept(Object JavaDoc proxy, Method JavaDoc method, Object JavaDoc[] args, MethodProxy methodProxy) {
515             Object JavaDoc other = args[0];
516             if (proxy == other) {
517                 return Boolean.TRUE;
518             }
519             AdvisedSupport otherAdvised = null;
520             if (other instanceof Factory) {
521                 Callback callback = ((Factory) other).getCallback(INVOKE_EQUALS);
522                 if (!(callback instanceof EqualsInterceptor)) {
523                     return Boolean.FALSE;
524                 }
525                 otherAdvised = ((EqualsInterceptor) callback).advised;
526             }
527             else {
528                 return Boolean.FALSE;
529             }
530             return (AopProxyUtils.equalsInProxy(this.advised, otherAdvised) ? Boolean.TRUE : Boolean.FALSE);
531         }
532     }
533
534
535     /**
536      * Dispatcher for the <code>hashCode</code> method.
537      * Ensures that the method call is always handled by this class.
538      */

539     private static class HashCodeInterceptor implements MethodInterceptor, Serializable JavaDoc {
540
541         private final AdvisedSupport advised;
542
543         public HashCodeInterceptor(AdvisedSupport advised) {
544             this.advised = advised;
545         }
546
547         public Object JavaDoc intercept(Object JavaDoc proxy, Method JavaDoc method, Object JavaDoc[] args, MethodProxy methodProxy) {
548             return new Integer JavaDoc(Cglib2AopProxy.class.hashCode() * 13 + this.advised.getTargetSource().hashCode());
549         }
550     }
551
552
553     /**
554      * Interceptor used specifically for advised methods on a frozen, static proxy.
555      */

556     private static class FixedChainStaticTargetInterceptor implements MethodInterceptor, Serializable JavaDoc {
557
558         private final List JavaDoc adviceChain;
559
560         private final Object JavaDoc target;
561
562         private final Class JavaDoc targetClass;
563
564         public FixedChainStaticTargetInterceptor(List JavaDoc adviceChain, Object JavaDoc target, Class JavaDoc targetClass) {
565             this.adviceChain = adviceChain;
566             this.target = target;
567             this.targetClass = targetClass;
568         }
569
570         public Object JavaDoc intercept(Object JavaDoc proxy, Method JavaDoc method, Object JavaDoc[] args, MethodProxy methodProxy) throws Throwable JavaDoc {
571             Object JavaDoc retVal = null;
572             MethodInvocation invocation = new CglibMethodInvocation(proxy, this.target, method, args,
573                     this.targetClass, this.adviceChain, methodProxy);
574             // If we get here, we need to create a MethodInvocation.
575
retVal = invocation.proceed();
576             retVal = massageReturnTypeIfNecessary(proxy, this.target, retVal);
577             return retVal;
578         }
579     }
580
581
582     /**
583      * General purpose AOP callback. Used when the target is dynamic or when the
584      * proxy is not frozen.
585      */

586     private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable JavaDoc {
587
588         private AdvisedSupport advised;
589
590         public DynamicAdvisedInterceptor(AdvisedSupport advised) {
591             this.advised = advised;
592         }
593
594         public Object JavaDoc intercept(Object JavaDoc proxy, Method JavaDoc method, Object JavaDoc[] args, MethodProxy methodProxy) throws Throwable JavaDoc {
595             MethodInvocation invocation = null;
596             Object JavaDoc oldProxy = null;
597             boolean setProxyContext = false;
598             Class JavaDoc targetClass = null;
599             Object JavaDoc target = null;
600             try {
601                 Object JavaDoc retVal = null;
602                 if (this.advised.exposeProxy) {
603                     // Make invocation available if necessary.
604
oldProxy = AopContext.setCurrentProxy(proxy);
605                     setProxyContext = true;
606                 }
607                 // May be <code>null</code>. Get as late as possible to minimize the time we
608
// "own" the target, in case it comes from a pool.
609
target = getTarget();
610                 if (target != null) {
611                     targetClass = target.getClass();
612                 }
613                 List JavaDoc chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
614                 // Check whether we only have one InvokerInterceptor: that is,
615
// no real advice, but just reflective invocation of the target.
616
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
617                     // We can skip creating a MethodInvocation: just invoke the target directly.
618
// Note that the final invoker must be an InvokerInterceptor, so we know
619
// it does nothing but a reflective operation on the target, and no hot
620
// swapping or fancy proxying.
621
retVal = methodProxy.invoke(target, args);
622                 }
623                 else {
624                     // We need to create a method invocation...
625
invocation = new CglibMethodInvocation(proxy, target, method, args,
626                             targetClass, chain, methodProxy);
627                     // If we get here, we need to create a MethodInvocation.
628
retVal = invocation.proceed();
629                 }
630
631                 retVal = massageReturnTypeIfNecessary(proxy, target, retVal);
632                 return retVal;
633             }
634             finally {
635                 if (target != null) {
636                     releaseTarget(target);
637                 }
638                 if (setProxyContext) {
639                     // Restore old proxy.
640
AopContext.setCurrentProxy(oldProxy);
641                 }
642             }
643         }
644
645         public boolean equals(Object JavaDoc other) {
646             return (this == other ||
647                     (other instanceof DynamicAdvisedInterceptor &&
648                             this.advised.equals(((DynamicAdvisedInterceptor) other).advised)));
649         }
650
651         /**
652          * CGLIB uses this to drive proxy creation.
653          */

654         public int hashCode() {
655             return this.advised.hashCode();
656         }
657
658         protected Object JavaDoc getTarget() throws Exception JavaDoc {
659             return this.advised.getTargetSource().getTarget();
660         }
661
662         protected void releaseTarget(Object JavaDoc target) throws Exception JavaDoc {
663             this.advised.getTargetSource().releaseTarget(target);
664         }
665     }
666
667
668     /**
669      * Implementation of AOP Alliance MethodInvocation used by this AOP proxy.
670      */

671     private static class CglibMethodInvocation extends ReflectiveMethodInvocation {
672
673         private final MethodProxy methodProxy;
674
675         private boolean protectedMethod;
676
677         public CglibMethodInvocation(Object JavaDoc proxy, Object JavaDoc target, Method JavaDoc method, Object JavaDoc[] arguments,
678                 Class JavaDoc targetClass, List JavaDoc interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) {
679             super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers);
680             this.methodProxy = methodProxy;
681             this.protectedMethod = Modifier.isProtected(method.getModifiers());
682         }
683
684         /**
685          * Gives a marginal performance improvement versus using reflection to
686          * invoke the target when invoking public methods.
687          */

688         protected Object JavaDoc invokeJoinpoint() throws Throwable JavaDoc {
689             if (this.protectedMethod) {
690                 return super.invokeJoinpoint();
691             }
692             else {
693                 return this.methodProxy.invoke(this.target, this.arguments);
694             }
695         }
696     }
697
698
699     /**
700      * CallbackFilter to assign Callbacks to methods.
701      */

702     private static class ProxyCallbackFilter implements CallbackFilter {
703
704         private final AdvisedSupport advised;
705
706         private final Map JavaDoc fixedInterceptorMap;
707
708         private final int fixedInterceptorOffset;
709
710         public ProxyCallbackFilter(AdvisedSupport advised, Map JavaDoc fixedInterceptorMap, int fixedInterceptorOffset) {
711             this.advised = advised;
712             this.fixedInterceptorMap = fixedInterceptorMap;
713             this.fixedInterceptorOffset = fixedInterceptorOffset;
714         }
715
716         /**
717          * Implementation of CallbackFilter.accept() to return the index of the
718          * callback we need.
719          * <p>The callbacks for each proxy are built up of a set of fixed callbacks
720          * for general use and then a set of callbacks that are specific to a method
721          * for use on static targets with a fixed advice chain.
722          * <p>The callback used is determined thus:
723          * <dl>
724          * <dt>For exposed proxies</dt>
725          * <dd>Exposing the proxy requires code to execute before and after the
726          * method/chain invocation. This means we must use
727          * DynamicAdvisedInterceptor, since all other interceptors can avoid the
728          * need for a try/catch block</dd>
729          * <dt>For Object.finalize():</dt>
730          * <dd>No override for this method is used</dd>
731          * <dt>For equals():</dt>
732          * <dd>The EqualsInterceptor is used to redirect equals() calls to a
733          * special handler to this proxy.</dd>
734          * <dt>For methods on the Advised class:</dt>
735          * <dd>the AdvisedDispatcher is used to dispatch the call directly to
736          * the target</dd>
737          * <dt>For advised methods:</dt>
738          * <dd>If the target is static and the advice chain is frozen then a
739          * FixedChainStaticTargetInterceptor specific to the method is used to
740          * invoke the advice chain. Otherwise a DyanmicAdvisedInterceptor is
741          * used.</dd>
742          * <dt>For non-advised methods:</dt>
743          * <dd>Where it can be determined that the method will not return <code>this</code>
744          * or when <code>ProxyFactory.getExposeProxy()</code> returns <code>false</code>,
745          * then a Dispatcher is used. For static targets, the StaticDispatcher is used;
746          * and for dynamic targets, a DynamicUnadvisedInterceptor is used.
747          * If it possible for the method to return <code>this</code> then a
748          * StaticUnadvisedInterceptor is used for static targets - the
749          * DynamicUnadvisedInterceptor already considers this.</dd>
750          * </dl>
751          */

752         public int accept(Method JavaDoc method) {
753             if (method.getDeclaringClass() == Object JavaDoc.class && method.getName().equals("finalize")) {
754                 logger.debug("Found finalize() method - using NO_OVERRIDE");
755                 return NO_OVERRIDE;
756             }
757             if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
758                     method.getDeclaringClass().isAssignableFrom(Advised.class)) {
759                 if (logger.isDebugEnabled()) {
760                     logger.debug("Method is declared on Advised interface: " + method);
761                 }
762                 return DISPATCH_ADVISED;
763             }
764             // We must always proxy equals, to direct calls to this.
765
if (AopUtils.isEqualsMethod(method)) {
766                 logger.debug("Found 'equals' method: " + method);
767                 return INVOKE_EQUALS;
768             }
769             // We must always calculate hashCode based on the proxy.
770
if (AopUtils.isHashCodeMethod(method)) {
771                 logger.debug("Found 'hashCode' method: " + method);
772                 return INVOKE_HASHCODE;
773             }
774             Class JavaDoc targetClass = this.advised.getTargetClass();
775             // Proxy is not yet available, but that shouldn't matter.
776
List JavaDoc chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
777             boolean haveAdvice = !chain.isEmpty();
778             boolean exposeProxy = this.advised.isExposeProxy();
779             boolean isStatic = this.advised.getTargetSource().isStatic();
780             boolean isFrozen = this.advised.isFrozen();
781             if (haveAdvice || !isFrozen) {
782                 // If exposing the proxy, then AOP_PROXY must be used.
783
if (exposeProxy) {
784                     if (logger.isDebugEnabled()) {
785                         logger.debug("Must expose proxy on advised method: " + method);
786                     }
787                     return AOP_PROXY;
788                 }
789                 String JavaDoc key = method.toString();
790                 // Check to see if we have fixed interceptor to serve this method.
791
// Else use the AOP_PROXY.
792
if (isStatic && isFrozen && this.fixedInterceptorMap.containsKey(key)) {
793                     if (logger.isDebugEnabled()) {
794                         logger.debug("Method has advice and optimisations are enabled: " + method);
795                     }
796                     // We know that we are optimising so we can use the
797
// FixedStaticChainInterceptors.
798
int index = ((Integer JavaDoc) this.fixedInterceptorMap.get(key)).intValue();
799                     return (index + fixedInterceptorOffset);
800                 }
801                 else {
802                     if (logger.isDebugEnabled()) {
803                         logger.debug("Unable to apply any optimisations to advised method: " + method);
804                     }
805                     return AOP_PROXY;
806                 }
807             }
808             else {
809                 // See if the return type of the method is outside the class hierarchy
810
// of the target type. If so we know it never needs to have return type
811
// massage and can use a dispatcher.
812
// If the proxy is being exposed, then must use the interceptor the
813
// correct one is already configured. If the target is not static cannot
814
// use a Dispatcher because the target can not then be released.
815
if (exposeProxy || !isStatic) {
816                     return INVOKE_TARGET;
817                 }
818                 Class JavaDoc returnType = method.getReturnType();
819                 if (targetClass == returnType) {
820                     if (logger.isDebugEnabled()) {
821                         logger.debug("Method " + method +
822                                 "has return type same as target type (may return this) - using INVOKE_TARGET");
823                     }
824                     return INVOKE_TARGET;
825                 }
826                 else if (returnType.isPrimitive() || !returnType.isAssignableFrom(targetClass)) {
827                     if (logger.isDebugEnabled()) {
828                         logger.debug("Method " + method +
829                                 " has return type that ensures this cannot be returned- using DISPATCH_TARGET");
830                     }
831                     return DISPATCH_TARGET;
832                 }
833                 else {
834                     if (logger.isDebugEnabled()) {
835                         logger.debug("Method " + method +
836                                 "has return type that is assignable from the target type (may return this) - " +
837                                 "using INVOKE_TARGET");
838                     }
839                     return INVOKE_TARGET;
840                 }
841             }
842         }
843
844         public boolean equals(Object JavaDoc other) {
845             if (other == this) {
846                 return true;
847             }
848             if (!(other instanceof ProxyCallbackFilter)) {
849                 return false;
850             }
851             ProxyCallbackFilter otherCallbackFilter = (ProxyCallbackFilter) other;
852             if (this.advised.isFrozen() != otherCallbackFilter.advised.isFrozen()) {
853                 return false;
854             }
855             if (this.advised.isExposeProxy() != otherCallbackFilter.advised.isExposeProxy()) {
856                 return false;
857             }
858             if (this.advised.getTargetSource().isStatic() !=
859                     otherCallbackFilter.advised.getTargetSource().isStatic()) {
860                 return false;
861             }
862             if (!AopProxyUtils.equalsProxiedInterfaces(this.advised, otherCallbackFilter.advised)) {
863                 return false;
864             }
865             // Advice instance identity is unimportant to the proxy class:
866
// All that matters is type and ordering.
867
Advisor[] thisAdvisors = this.advised.getAdvisors();
868             Advisor[] thatAdvisors = otherCallbackFilter.advised.getAdvisors();
869             if (thisAdvisors.length != thatAdvisors.length) {
870                 return false;
871             }
872             for (int i = 0; i < thisAdvisors.length; i++) {
873                 Advisor thisAdvisor = thisAdvisors[i];
874                 Advisor thatAdvisor = thatAdvisors[i];
875                 if (!equalsAdviceClasses(thisAdvisor, thatAdvisor)) {
876                     return false;
877                 }
878                 if (!equalsPointcuts(thisAdvisor, thatAdvisor)) {
879                     return false;
880                 }
881             }
882             return true;
883         }
884
885         private boolean equalsAdviceClasses(Advisor a, Advisor b) {
886             Advice aa = a.getAdvice();
887             Advice ba = b.getAdvice();
888             if (aa == null || ba == null) {
889                 return (aa == ba);
890             }
891             return aa.getClass().equals(ba.getClass());
892         }
893
894         private boolean equalsPointcuts(Advisor a, Advisor b) {
895             return (a instanceof PointcutAdvisor && b instanceof PointcutAdvisor &&
896                     ObjectUtils.nullSafeEquals(((PointcutAdvisor) a).getPointcut(), ((PointcutAdvisor) b).getPointcut()));
897         }
898
899         public int hashCode() {
900             int hashCode = 0;
901             Advisor[] advisors = this.advised.getAdvisors();
902             for (int i = 0; i < advisors.length; i++) {
903                 Advice advice = advisors[i].getAdvice();
904                 if (advice != null) {
905                     hashCode = 13 * hashCode + advice.getClass().hashCode();
906                 }
907             }
908             hashCode = 13 * hashCode + (this.advised.isFrozen() ? 1 : 0);
909             hashCode = 13 * hashCode + (this.advised.isExposeProxy() ? 1 : 0);
910             hashCode = 13 * hashCode + (this.advised.isOptimize() ? 1 : 0);
911             hashCode = 13 * hashCode + (this.advised.isOpaque() ? 1 : 0);
912             return hashCode;
913         }
914     }
915
916 }
917
Popular Tags