1 /*
2 * Copyright 2002-2008 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;
20 import java.lang.reflect.Method;
21 import java.lang.reflect.Modifier;
22 import java.lang.reflect.UndeclaredThrowableException;
23 import java.util.HashMap;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.WeakHashMap;
27
28 import net.sf.cglib.core.CodeGenerationException;
29 import net.sf.cglib.proxy.Callback;
30 import net.sf.cglib.proxy.CallbackFilter;
31 import net.sf.cglib.proxy.Dispatcher;
32 import net.sf.cglib.proxy.Enhancer;
33 import net.sf.cglib.proxy.Factory;
34 import net.sf.cglib.proxy.MethodInterceptor;
35 import net.sf.cglib.proxy.MethodProxy;
36 import net.sf.cglib.proxy.NoOp;
37 import net.sf.cglib.transform.impl.UndeclaredThrowableStrategy;
38 import org.aopalliance.aop.Advice;
39 import org.aopalliance.intercept.MethodInvocation;
40 import org.apache.commons.logging.Log;
41 import org.apache.commons.logging.LogFactory;
42
43 import org.springframework.aop.Advisor;
44 import org.springframework.aop.PointcutAdvisor;
45 import org.springframework.aop.RawTargetAccess;
46 import org.springframework.aop.TargetSource;
47 import org.springframework.aop.support.AopUtils;
48 import org.springframework.core.SmartClassLoader;
49 import org.springframework.util.Assert;
50 import org.springframework.util.ObjectUtils;
51
52 /**
53 * CGLIB2-based {@link AopProxy} implementation for the Spring AOP framework.
54 *
55 * <p><i>Requires CGLIB 2.1+ on the classpath.</i>.
56 * As of Spring 2.0, earlier CGLIB versions are not supported anymore.
57 *
58 * <p>Objects of this type should be obtained through proxy factories,
59 * configured by an {@link AdvisedSupport} object. This class is internal
60 * to Spring's AOP framework and need not be used directly by client code.
61 *
62 * <p>{@link DefaultAopProxyFactory} will automatically create CGLIB2-based
63 * proxies if necessary, for example in case of proxying a target class
64 * (see the {@link DefaultAopProxyFactory attendant javadoc} for details).
65 *
66 * <p>Proxies created using this class are thread-safe if the underlying
67 * (target) class is thread-safe.
68 *
69 * @author Rod Johnson
70 * @author Rob Harrop
71 * @author Juergen Hoeller
72 * @author Ramnivas Laddad
73 * @see net.sf.cglib.proxy.Enhancer
74 * @see AdvisedSupport#setProxyTargetClass
75 * @see DefaultAopProxyFactory
76 */
77 final class Cglib2AopProxy implements AopProxy, Serializable {
78
79 // Constants for CGLIB callback array indices
80 private static final int AOP_PROXY = 0;
81 private static final int INVOKE_TARGET = 1;
82 private static final int NO_OVERRIDE = 2;
83 private static final int DISPATCH_TARGET = 3;
84 private static final int DISPATCH_ADVISED = 4;
85 private static final int INVOKE_EQUALS = 5;
86 private static final int INVOKE_HASHCODE = 6;
87
88
89 /** Logger available to subclasses; static to optimize serialization */
90 protected final static Log logger = LogFactory.getLog(Cglib2AopProxy.class);
91
92 /** Keeps track of the Classes that we have validated for final methods */
93 private static final Map validatedClasses = new WeakHashMap();
94
95
96 /** The configuration used to configure this proxy */
97 protected final AdvisedSupport advised;
98
99 private Object[] constructorArgs;
100
101 private Class[] constructorArgTypes;
102
103 /** Dispatcher used for methods on Advised */
104 private final transient AdvisedDispatcher advisedDispatcher;
105
106 private transient Map fixedInterceptorMap;
107
108 private transient int fixedInterceptorOffset;
109
110
111 /**
112 * Create a new Cglib2AopProxy for the given AOP configuration.
113 * @param config the AOP configuration as AdvisedSupport object
114 * @throws AopConfigException if the config is invalid. We try to throw an informative
115 * exception in this case, rather than let a mysterious failure happen later.
116 */
117 public Cglib2AopProxy(AdvisedSupport config) throws AopConfigException {
118 Assert.notNull(config, "AdvisedSupport must not be null");
119 if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
120 throw new AopConfigException("No advisors and no TargetSource specified");
121 }
122 this.advised = config;
123 this.advisedDispatcher = new AdvisedDispatcher(this.advised);
124 }
125
126 /**
127 * Set constructor arguments to use for creating the proxy.
128 * @param constructorArgs the constructor argument values
129 * @param constructorArgTypes the constructor argument types
130 */
131 public void setConstructorArguments(Object[] constructorArgs, Class[] constructorArgTypes) {
132 if (constructorArgs == null || constructorArgTypes == null) {
133 throw new IllegalArgumentException("Both 'constructorArgs' and 'constructorArgTypes' need to be specified");
134 }
135 if (constructorArgs.length != constructorArgTypes.length) {
136 throw new IllegalArgumentException("Number of 'constructorArgs' (" + constructorArgs.length +
137 ") must match number of 'constructorArgTypes' (" + constructorArgTypes.length + ")");
138 }
139 this.constructorArgs = constructorArgs;
140 this.constructorArgTypes = constructorArgTypes;
141 }
142
143
144 public Object getProxy() {
145 return getProxy(null);
146 }
147
148 public Object getProxy(ClassLoader classLoader) {
149 if (logger.isDebugEnabled()) {
150 logger.debug("Creating CGLIB2 proxy: target source is " + this.advised.getTargetSource());
151 }
152
153 try {
154 Class rootClass = this.advised.getTargetClass();
155 Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
156
157 Class proxySuperClass = rootClass;
158 if (AopUtils.isCglibProxyClass(rootClass)) {
159 proxySuperClass = rootClass.getSuperclass();
160 Class[] additionalInterfaces = rootClass.getInterfaces();
161 for (int i = 0; i < additionalInterfaces.length; i++) {
162 Class additionalInterface = additionalInterfaces[i];
163 this.advised.addInterface(additionalInterface);
164 }
165 }
166
167 // Validate the class, writing log messages as necessary.
168 validateClassIfNecessary(proxySuperClass);
169
170 // Configure CGLIB Enhancer...
171 Enhancer enhancer = createEnhancer();
172 if (classLoader != null) {
173 enhancer.setClassLoader(classLoader);
174 if (classLoader instanceof SmartClassLoader &&
175 ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
176 enhancer.setUseCache(false);
177 }
178 }
179 enhancer.setSuperclass(proxySuperClass);
180 enhancer.setStrategy(new UndeclaredThrowableStrategy(UndeclaredThrowableException.class));
181 enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
182 enhancer.setInterceptDuringConstruction(false);
183
184 Callback[] callbacks = getCallbacks(rootClass);
185 enhancer.setCallbacks(callbacks);
186 enhancer.setCallbackFilter(new ProxyCallbackFilter(
187 this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
188
189 Class[] types = new Class[callbacks.length];
190 for (int x = 0; x < types.length; x++) {
191 types[x] = callbacks[x].getClass();
192 }
193 enhancer.setCallbackTypes(types);
194
195 // Generate the proxy class and create a proxy instance.
196 Object proxy;
197 if (this.constructorArgs != null) {
198 proxy = enhancer.create(this.constructorArgTypes, this.constructorArgs);
199 }
200 else {
201 proxy = enhancer.create();
202 }
203
204 return proxy;
205 }
206 catch (CodeGenerationException ex) {
207 throw new AopConfigException("Could not 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 (IllegalArgumentException ex) {
213 throw new AopConfigException("Could not generate CGLIB subclass of class [" +
214 this.advised.getTargetClass() + "]: " +
215 "Common causes of this problem include using a final class or a non-visible class",
216 ex);
217 }
218 catch (Exception ex) {
219 // TargetSource.getTarget() failed
220 throw new AopConfigException("Unexpected AOP exception", ex);
221 }
222 }
223
224 /**
225 * Creates the CGLIB {@link Enhancer}. Subclasses may wish to override this to return a custom
226 * {@link Enhancer} implementation.
227 */
228 protected Enhancer createEnhancer() {
229 return new Enhancer();
230 }
231
232 /**
233 * Checks to see whether the supplied <code>Class</code> has already been validated and
234 * validates it if not.
235 */
236 private void validateClassIfNecessary(Class proxySuperClass) {
237 if (logger.isWarnEnabled()) {
238 synchronized (validatedClasses) {
239 if (!validatedClasses.containsKey(proxySuperClass)) {
240 doValidateClass(proxySuperClass);
241 validatedClasses.put(proxySuperClass, Boolean.TRUE);
242 }
243 }
244 }
245 }
246
247 /**
248 * Checks for final methods on the <code>Class</code> and writes warnings to the log
249 * for each one found.
250 */
251 private void doValidateClass(Class proxySuperClass) {
252 Method[] methods = proxySuperClass.getMethods();
253 for (int i = 0; i < methods.length; i++) {
254 Method method = methods[i];
255 if (!Object.class.equals(method.getDeclaringClass()) && Modifier.isFinal(method.getModifiers())) {
256 logger.warn("Unable to proxy method [" + method + "] because it is final: " +
257 "All calls to this method via a proxy will be routed directly to the proxy.");
258 }
259 }
260 }
261
262 private Callback[] getCallbacks(Class rootClass) throws Exception {
263 // Parameters used for optimisation choices...
264 boolean exposeProxy = this.advised.isExposeProxy();
265 boolean isFrozen = this.advised.isFrozen();
266 boolean isStatic = this.advised.getTargetSource().isStatic();
267
268 // Choose an "aop" interceptor (used for AOP calls).
269 Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
270
271 // Choose a "straight to target" interceptor. (used for calls that are
272 // unadvised but can return this). May be required to expose the proxy.
273 Callback targetInterceptor = null;
274
275 if (exposeProxy) {
276 targetInterceptor = isStatic ?
277 (Callback) new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) :
278 (Callback) new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource());
279 }
280 else {
281 targetInterceptor = isStatic ?
282 (Callback) new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :
283 (Callback) new DynamicUnadvisedInterceptor(this.advised.getTargetSource());
284 }
285
286 // Choose a "direct to target" dispatcher (used for
287 // unadvised calls to static targets that cannot return this).
288 Callback targetDispatcher = isStatic ?
289 (Callback) new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp();
290
291 Callback[] mainCallbacks = new Callback[]{
292 aopInterceptor, // for normal advice
293 targetInterceptor, // invoke target without considering advice, if optimized
294 new SerializableNoOp(), // no override for methods mapped to this
295 targetDispatcher, this.advisedDispatcher,
296 new EqualsInterceptor(this.advised),
297 new HashCodeInterceptor(this.advised)
298 };
299
300 Callback[] callbacks;
301
302 // If the target is a static one and the advice chain is frozen,
303 // then we can make some optimisations by sending the AOP calls
304 // direct to the target using the fixed chain for that method.
305 if (isStatic && isFrozen) {
306 Method[] methods = rootClass.getMethods();
307 Callback[] fixedCallbacks = new Callback[methods.length];
308 this.fixedInterceptorMap = new HashMap(methods.length);
309
310 // TODO: small memory optimisation here (can skip creation for
311 // methods with no advice)
312 for (int x = 0; x < methods.length; x++) {
313 List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(methods[x], rootClass);
314 fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(
315 chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass());
316 this.fixedInterceptorMap.put(methods[x].toString(), new Integer(x));
317 }
318
319 // Now copy both the callbacks from mainCallbacks
320 // and fixedCallbacks into the callbacks array.
321 callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length];
322
323 for (int x = 0; x < mainCallbacks.length; x++) {
324 callbacks[x] = mainCallbacks[x];
325 }
326
327 for (int x = 0; x < fixedCallbacks.length; x++) {
328 callbacks[x + mainCallbacks.length] = fixedCallbacks[x];
329 }
330
331 this.fixedInterceptorOffset = mainCallbacks.length;
332 }
333 else {
334 callbacks = mainCallbacks;
335 }
336 return callbacks;
337 }
338
339 /**
340 * Wrap a return of this if necessary to be the proxy
341 */
342 private static Object massageReturnTypeIfNecessary(Object proxy, Object target, Method method, Object retVal) {
343 // Massage return value if necessary
344 if (retVal != null && retVal == target &&
345 !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
346 // Special case: it returned "this".
347 // Note that we can't help if the target sets a reference
348 // to itself in another returned object.
349 retVal = proxy;
350 }
351 return retVal;
352 }
353
354
355 public boolean equals(Object other) {
356 return (this == other || (other instanceof Cglib2AopProxy &&
357 AopProxyUtils.equalsInProxy(this.advised, ((Cglib2AopProxy) other).advised)));
358 }
359
360 public int hashCode() {
361 return Cglib2AopProxy.class.hashCode() * 13 + this.advised.getTargetSource().hashCode();
362 }
363
364
365 /**
366 * Serializable replacement for CGLIB's NoOp interface.
367 * Public to allow use elsewhere in the framework.
368 */
369 public static class SerializableNoOp implements NoOp, Serializable {
370 }
371
372
373 /**
374 * Method interceptor used for static targets with no advice chain. The call
375 * is passed directly back to the target. Used when the proxy needs to be
376 * exposed and it can't be determined that the method won't return
377 * <code>this</code>.
378 */
379 private static class StaticUnadvisedInterceptor implements MethodInterceptor, Serializable {
380
381 private final Object target;
382
383 public StaticUnadvisedInterceptor(Object target) {
384 this.target = target;
385 }
386
387 public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
388 Object retVal = methodProxy.invoke(this.target, args);
389 return massageReturnTypeIfNecessary(proxy, this.target, method, retVal);
390 }
391 }
392
393
394 /**
395 * Method interceptor used for static targets with no advice chain, when the
396 * proxy is to be exposed.
397 */
398 private static class StaticUnadvisedExposedInterceptor implements MethodInterceptor, Serializable {
399
400 private final Object target;
401
402 public StaticUnadvisedExposedInterceptor(Object target) {
403 this.target = target;
404 }
405
406 public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
407 Object oldProxy = null;
408 try {
409 oldProxy = AopContext.setCurrentProxy(proxy);
410 Object retVal = methodProxy.invoke(this.target, args);
411 return massageReturnTypeIfNecessary(proxy, this.target, method, retVal);
412 }
413 finally {
414 AopContext.setCurrentProxy(oldProxy);
415 }
416 }
417 }
418
419
420 /**
421 * Interceptor used to invoke a dynamic target without creating a method
422 * invocation or evaluating an advice chain. (We know there was no advice
423 * for this method.)
424 */
425 private static class DynamicUnadvisedInterceptor implements MethodInterceptor, Serializable {
426
427 private final TargetSource targetSource;
428
429 public DynamicUnadvisedInterceptor(TargetSource targetSource) {
430 this.targetSource = targetSource;
431 }
432
433 public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
434 Object target = this.targetSource.getTarget();
435 try {
436 Object retVal = methodProxy.invoke(target, args);
437 return massageReturnTypeIfNecessary(proxy, target, method, retVal);
438 }
439 finally {
440 this.targetSource.releaseTarget(target);
441 }
442 }
443 }
444
445
446 /**
447 * Interceptor for unadvised dynamic targets when the proxy needs exposing.
448 */
449 private static class DynamicUnadvisedExposedInterceptor implements MethodInterceptor, Serializable {
450
451 private final TargetSource targetSource;
452
453 public DynamicUnadvisedExposedInterceptor(TargetSource targetSource) {
454 this.targetSource = targetSource;
455 }
456
457 public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
458 Object oldProxy = null;
459 Object target = this.targetSource.getTarget();
460 try {
461 oldProxy = AopContext.setCurrentProxy(proxy);
462 Object retVal = methodProxy.invoke(target, args);
463 return massageReturnTypeIfNecessary(proxy, target, method, retVal);
464 }
465 finally {
466 AopContext.setCurrentProxy(oldProxy);
467 this.targetSource.releaseTarget(target);
468 }
469 }
470 }
471
472
473 /**
474 * Dispatcher for a static target. Dispatcher is much faster than
475 * interceptor. This will be used whenever it can be determined that a
476 * method definitely does not return "this"
477 */
478 private static class StaticDispatcher implements Dispatcher, Serializable {
479
480 private Object target;
481
482 public StaticDispatcher(Object target) {
483 this.target = target;
484 }
485
486 public Object loadObject() {
487 return this.target;
488 }
489 }
490
491
492 /**
493 * Dispatcher for any methods declared on the Advised class.
494 */
495 private static class AdvisedDispatcher implements Dispatcher, Serializable {
496
497 private final AdvisedSupport advised;
498
499 public AdvisedDispatcher(AdvisedSupport advised) {
500 this.advised = advised;
501 }
502
503 public Object loadObject() throws Exception {
504 return this.advised;
505 }
506 }
507
508
509 /**
510 * Dispatcher for the <code>equals</code> method.
511 * Ensures that the method call is always handled by this class.
512 */
513 private static class EqualsInterceptor implements MethodInterceptor, Serializable {
514
515 private final AdvisedSupport advised;
516
517 public EqualsInterceptor(AdvisedSupport advised) {
518 this.advised = advised;
519 }
520
521 public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) {
522 Object other = args[0];
523 if (proxy == other) {
524 return Boolean.TRUE;
525 }
526 AdvisedSupport otherAdvised = null;
527 if (other instanceof Factory) {
528 Callback callback = ((Factory) other).getCallback(INVOKE_EQUALS);
529 if (!(callback instanceof EqualsInterceptor)) {
530 return Boolean.FALSE;
531 }
532 otherAdvised = ((EqualsInterceptor) callback).advised;
533 }
534 else {
535 return Boolean.FALSE;
536 }
537 return (AopProxyUtils.equalsInProxy(this.advised, otherAdvised) ? Boolean.TRUE : Boolean.FALSE);
538 }
539 }
540
541
542 /**
543 * Dispatcher for the <code>hashCode</code> method.
544 * Ensures that the method call is always handled by this class.
545 */
546 private static class HashCodeInterceptor implements MethodInterceptor, Serializable {
547
548 private final AdvisedSupport advised;
549
550 public HashCodeInterceptor(AdvisedSupport advised) {
551 this.advised = advised;
552 }
553
554 public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) {
555 return new Integer(Cglib2AopProxy.class.hashCode() * 13 + this.advised.getTargetSource().hashCode());
556 }
557 }
558
559
560 /**
561 * Interceptor used specifically for advised methods on a frozen, static proxy.
562 */
563 private static class FixedChainStaticTargetInterceptor implements MethodInterceptor, Serializable {
564
565 private final List adviceChain;
566
567 private final Object target;
568
569 private final Class targetClass;
570
571 public FixedChainStaticTargetInterceptor(List adviceChain, Object target, Class targetClass) {
572 this.adviceChain = adviceChain;
573 this.target = target;
574 this.targetClass = targetClass;
575 }
576
577 public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
578 Object retVal = null;
579 MethodInvocation invocation = new CglibMethodInvocation(proxy, this.target, method, args,
580 this.targetClass, this.adviceChain, methodProxy);
581 // If we get here, we need to create a MethodInvocation.
582 retVal = invocation.proceed();
583 retVal = massageReturnTypeIfNecessary(proxy, this.target, method, retVal);
584 return retVal;
585 }
586 }
587
588
589 /**
590 * General purpose AOP callback. Used when the target is dynamic or when the
591 * proxy is not frozen.
592 */
593 private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {
594
595 private AdvisedSupport advised;
596
597 public DynamicAdvisedInterceptor(AdvisedSupport advised) {
598 this.advised = advised;
599 }
600
601 public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
602 MethodInvocation invocation = null;
603 Object oldProxy = null;
604 boolean setProxyContext = false;
605 Class targetClass = null;
606 Object target = null;
607 try {
608 Object retVal = null;
609 if (this.advised.exposeProxy) {
610 // Make invocation available if necessary.
611 oldProxy = AopContext.setCurrentProxy(proxy);
612 setProxyContext = true;
613 }
614 // May be <code>null</code>. Get as late as possible to minimize the time we
615 // "own" the target, in case it comes from a pool.
616 target = getTarget();
617 if (target != null) {
618 targetClass = target.getClass();
619 }
620 List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
621 // Check whether we only have one InvokerInterceptor: that is,
622 // no real advice, but just reflective invocation of the target.
623 if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
624 // We can skip creating a MethodInvocation: just invoke the target directly.
625 // Note that the final invoker must be an InvokerInterceptor, so we know
626 // it does nothing but a reflective operation on the target, and no hot
627 // swapping or fancy proxying.
628 retVal = methodProxy.invoke(target, args);
629 }
630 else {
631 // We need to create a method invocation...
632 invocation = new CglibMethodInvocation(proxy, target, method, args,
633 targetClass, chain, methodProxy);
634 // If we get here, we need to create a MethodInvocation.
635 retVal = invocation.proceed();
636 }
637
638 retVal = massageReturnTypeIfNecessary(proxy, target, method, retVal);
639 return retVal;
640 }
641 finally {
642 if (target != null) {
643 releaseTarget(target);
644 }
645 if (setProxyContext) {
646 // Restore old proxy.
647 AopContext.setCurrentProxy(oldProxy);
648 }
649 }
650 }
651
652 public boolean equals(Object other) {
653 return (this == other ||
654 (other instanceof DynamicAdvisedInterceptor &&
655 this.advised.equals(((DynamicAdvisedInterceptor) other).advised)));
656 }
657
658 /**
659 * CGLIB uses this to drive proxy creation.
660 */
661 public int hashCode() {
662 return this.advised.hashCode();
663 }
664
665 protected Object getTarget() throws Exception {
666 return this.advised.getTargetSource().getTarget();
667 }
668
669 protected void releaseTarget(Object target) throws Exception {
670 this.advised.getTargetSource().releaseTarget(target);
671 }
672 }
673
674
675 /**
676 * Implementation of AOP Alliance MethodInvocation used by this AOP proxy.
677 */
678 private static class CglibMethodInvocation extends ReflectiveMethodInvocation {
679
680 private final MethodProxy methodProxy;
681
682 private boolean protectedMethod;
683
684 public CglibMethodInvocation(Object proxy, Object target, Method method, Object[] arguments,
685 Class targetClass, List interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) {
686 super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers);
687 this.methodProxy = methodProxy;
688 this.protectedMethod = Modifier.isProtected(method.getModifiers());
689 }
690
691 /**
692 * Gives a marginal performance improvement versus using reflection to
693 * invoke the target when invoking public methods.
694 */
695 protected Object invokeJoinpoint() throws Throwable {
696 if (this.protectedMethod) {
697 return super.invokeJoinpoint();
698 }
699 else {
700 return this.methodProxy.invoke(this.target, this.arguments);
701 }
702 }
703 }
704
705
706 /**
707 * CallbackFilter to assign Callbacks to methods.
708 */
709 private static class ProxyCallbackFilter implements CallbackFilter {
710
711 private final AdvisedSupport advised;
712
713 private final Map fixedInterceptorMap;
714
715 private final int fixedInterceptorOffset;
716
717 public ProxyCallbackFilter(AdvisedSupport advised, Map fixedInterceptorMap, int fixedInterceptorOffset) {
718 this.advised = advised;
719 this.fixedInterceptorMap = fixedInterceptorMap;
720 this.fixedInterceptorOffset = fixedInterceptorOffset;
721 }
722
723 /**
724 * Implementation of CallbackFilter.accept() to return the index of the
725 * callback we need.
726 * <p>The callbacks for each proxy are built up of a set of fixed callbacks
727 * for general use and then a set of callbacks that are specific to a method
728 * for use on static targets with a fixed advice chain.
729 * <p>The callback used is determined thus:
730 * <dl>
731 * <dt>For exposed proxies</dt>
732 * <dd>Exposing the proxy requires code to execute before and after the
733 * method/chain invocation. This means we must use
734 * DynamicAdvisedInterceptor, since all other interceptors can avoid the
735 * need for a try/catch block</dd>
736 * <dt>For Object.finalize():</dt>
737 * <dd>No override for this method is used.</dd>
738 * <dt>For equals():</dt>
739 * <dd>The EqualsInterceptor is used to redirect equals() calls to a
740 * special handler to this proxy.</dd>
741 * <dt>For methods on the Advised class:</dt>
742 * <dd>the AdvisedDispatcher is used to dispatch the call directly to
743 * the target</dd>
744 * <dt>For advised methods:</dt>
745 * <dd>If the target is static and the advice chain is frozen then a
746 * FixedChainStaticTargetInterceptor specific to the method is used to
747 * invoke the advice chain. Otherwise a DyanmicAdvisedInterceptor is
748 * used.</dd>
749 * <dt>For non-advised methods:</dt>
750 * <dd>Where it can be determined that the method will not return <code>this</code>
751 * or when <code>ProxyFactory.getExposeProxy()</code> returns <code>false</code>,
752 * then a Dispatcher is used. For static targets, the StaticDispatcher is used;
753 * and for dynamic targets, a DynamicUnadvisedInterceptor is used.
754 * If it possible for the method to return <code>this</code> then a
755 * StaticUnadvisedInterceptor is used for static targets - the
756 * DynamicUnadvisedInterceptor already considers this.</dd>
757 * </dl>
758 */
759 public int accept(Method method) {
760 if (AopUtils.isFinalizeMethod(method)) {
761 logger.debug("Found finalize() method - using NO_OVERRIDE");
762 return NO_OVERRIDE;
763 }
764 if (!this.advised.isOpaque() && method.getDeclaringClass().isInterface() &&
765 method.getDeclaringClass().isAssignableFrom(Advised.class)) {
766 if (logger.isDebugEnabled()) {
767 logger.debug("Method is declared on Advised interface: " + method);
768 }
769 return DISPATCH_ADVISED;
770 }
771 // We must always proxy equals, to direct calls to this.
772 if (AopUtils.isEqualsMethod(method)) {
773 logger.debug("Found 'equals' method: " + method);
774 return INVOKE_EQUALS;
775 }
776 // We must always calculate hashCode based on the proxy.
777 if (AopUtils.isHashCodeMethod(method)) {
778 logger.debug("Found 'hashCode' method: " + method);
779 return INVOKE_HASHCODE;
780 }
781 Class targetClass = this.advised.getTargetClass();
782 // Proxy is not yet available, but that shouldn't matter.
783 List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
784 boolean haveAdvice = !chain.isEmpty();
785 boolean exposeProxy = this.advised.isExposeProxy();
786 boolean isStatic = this.advised.getTargetSource().isStatic();
787 boolean isFrozen = this.advised.isFrozen();
788 if (haveAdvice || !isFrozen) {
789 // If exposing the proxy, then AOP_PROXY must be used.
790 if (exposeProxy) {
791 if (logger.isDebugEnabled()) {
792 logger.debug("Must expose proxy on advised method: " + method);
793 }
794 return AOP_PROXY;
795 }
796 String key = method.toString();
797 // Check to see if we have fixed interceptor to serve this method.
798 // Else use the AOP_PROXY.
799 if (isStatic && isFrozen && this.fixedInterceptorMap.containsKey(key)) {
800 if (logger.isDebugEnabled()) {
801 logger.debug("Method has advice and optimisations are enabled: " + method);
802 }
803 // We know that we are optimising so we can use the
804 // FixedStaticChainInterceptors.
805 int index = ((Integer) this.fixedInterceptorMap.get(key)).intValue();
806 return (index + this.fixedInterceptorOffset);
807 }
808 else {
809 if (logger.isDebugEnabled()) {
810 logger.debug("Unable to apply any optimisations to advised method: " + method);
811 }
812 return AOP_PROXY;
813 }
814 }
815 else {
816 // See if the return type of the method is outside the class hierarchy
817 // of the target type. If so we know it never needs to have return type
818 // massage and can use a dispatcher.
819 // If the proxy is being exposed, then must use the interceptor the
820 // correct one is already configured. If the target is not static cannot
821 // use a Dispatcher because the target can not then be released.
822 if (exposeProxy || !isStatic) {
823 return INVOKE_TARGET;
824 }
825 Class returnType = method.getReturnType();
826 if (targetClass == returnType) {
827 if (logger.isDebugEnabled()) {
828 logger.debug("Method " + method +
829 "has return type same as target type (may return this) - using INVOKE_TARGET");
830 }
831 return INVOKE_TARGET;
832 }
833 else if (returnType.isPrimitive() || !returnType.isAssignableFrom(targetClass)) {
834 if (logger.isDebugEnabled()) {
835 logger.debug("Method " + method +
836 " has return type that ensures this cannot be returned- using DISPATCH_TARGET");
837 }
838 return DISPATCH_TARGET;
839 }
840 else {
841 if (logger.isDebugEnabled()) {
842 logger.debug("Method " + method +
843 "has return type that is assignable from the target type (may return this) - " +
844 "using INVOKE_TARGET");
845 }
846 return INVOKE_TARGET;
847 }
848 }
849 }
850
851 public boolean equals(Object other) {
852 if (other == this) {
853 return true;
854 }
855 if (!(other instanceof ProxyCallbackFilter)) {
856 return false;
857 }
858 ProxyCallbackFilter otherCallbackFilter = (ProxyCallbackFilter) other;
859 AdvisedSupport otherAdvised = otherCallbackFilter.advised;
860 if (this.advised == null || otherAdvised == null) {
861 return false;
862 }
863 if (this.advised.isFrozen() != otherAdvised.isFrozen()) {
864 return false;
865 }
866 if (this.advised.isExposeProxy() != otherAdvised.isExposeProxy()) {
867 return false;
868 }
869 if (this.advised.getTargetSource().isStatic() != otherAdvised.getTargetSource().isStatic()) {
870 return false;
871 }
872 if (!AopProxyUtils.equalsProxiedInterfaces(this.advised, otherAdvised)) {
873 return false;
874 }
875 // Advice instance identity is unimportant to the proxy class:
876 // All that matters is type and ordering.
877 Advisor[] thisAdvisors = this.advised.getAdvisors();
878 Advisor[] thatAdvisors = otherAdvised.getAdvisors();
879 if (thisAdvisors.length != thatAdvisors.length) {
880 return false;
881 }
882 for (int i = 0; i < thisAdvisors.length; i++) {
883 Advisor thisAdvisor = thisAdvisors[i];
884 Advisor thatAdvisor = thatAdvisors[i];
885 if (!equalsAdviceClasses(thisAdvisor, thatAdvisor)) {
886 return false;
887 }
888 if (!equalsPointcuts(thisAdvisor, thatAdvisor)) {
889 return false;
890 }
891 }
892 return true;
893 }
894
895 private boolean equalsAdviceClasses(Advisor a, Advisor b) {
896 Advice aa = a.getAdvice();
897 Advice ba = b.getAdvice();
898 if (aa == null || ba == null) {
899 return (aa == ba);
900 }
901 return aa.getClass().equals(ba.getClass());
902 }
903
904 private boolean equalsPointcuts(Advisor a, Advisor b) {
905 // If only one of the advisor (but not both) is PointcutAdvisor, then it is a mismatch.
906 // Takes care of the situations where an IntroductionAdvisor is used (see SPR-3959).
907 if (a instanceof PointcutAdvisor ^ b instanceof PointcutAdvisor) {
908 return false;
909 }
910 // If both are PointcutAdvisor, match their pointcuts.
911 if (a instanceof PointcutAdvisor && b instanceof PointcutAdvisor) {
912 return ObjectUtils.nullSafeEquals(((PointcutAdvisor) a).getPointcut(), ((PointcutAdvisor) b).getPointcut());
913 }
914 // If neither is PointcutAdvisor, then from the pointcut matching perspective, it is a match.
915 return true;
916 }
917
918 public int hashCode() {
919 int hashCode = 0;
920 Advisor[] advisors = this.advised.getAdvisors();
921 for (int i = 0; i < advisors.length; i++) {
922 Advice advice = advisors[i].getAdvice();
923 if (advice != null) {
924 hashCode = 13 * hashCode + advice.getClass().hashCode();
925 }
926 }
927 hashCode = 13 * hashCode + (this.advised.isFrozen() ? 1 : 0);
928 hashCode = 13 * hashCode + (this.advised.isExposeProxy() ? 1 : 0);
929 hashCode = 13 * hashCode + (this.advised.isOptimize() ? 1 : 0);
930 hashCode = 13 * hashCode + (this.advised.isOpaque() ? 1 : 0);
931 return hashCode;
932 }
933 }
934
935 }