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

package org.springframework.aop.framework;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;

import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.aop.Advisor;
import org.springframework.aop.AopInvocationException;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.aop.RawTargetAccess;
import org.springframework.aop.TargetSource;
import org.springframework.aop.support.AopUtils;
import org.springframework.cglib.core.ClassLoaderAwareGeneratorStrategy;
import org.springframework.cglib.core.CodeGenerationException;
import org.springframework.cglib.core.SpringNamingPolicy;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.CallbackFilter;
import org.springframework.cglib.proxy.Dispatcher;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.Factory;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.cglib.proxy.NoOp;
import org.springframework.core.KotlinDetector;
import org.springframework.core.SmartClassLoader;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;

CGLIB-based AopProxy implementation for the Spring AOP framework.

Objects of this type should be obtained through proxy factories, configured by an AdvisedSupport object. This class is internal to Spring's AOP framework and need not be used directly by client code.

DefaultAopProxyFactory will automatically create CGLIB-based proxies if necessary, for example in case of proxying a target class (see the attendant javadoc for details).

Proxies created using this class are thread-safe if the underlying (target) class is thread-safe.

Author:Rod Johnson, Rob Harrop, Juergen Hoeller, Ramnivas Laddad, Chris Beams, Dave Syer
See Also:
/** * CGLIB-based {@link AopProxy} implementation for the Spring AOP framework. * * <p>Objects of this type should be obtained through proxy factories, * configured by an {@link AdvisedSupport} object. This class is internal * to Spring's AOP framework and need not be used directly by client code. * * <p>{@link DefaultAopProxyFactory} will automatically create CGLIB-based * proxies if necessary, for example in case of proxying a target class * (see the {@link DefaultAopProxyFactory attendant javadoc} for details). * * <p>Proxies created using this class are thread-safe if the underlying * (target) class is thread-safe. * * @author Rod Johnson * @author Rob Harrop * @author Juergen Hoeller * @author Ramnivas Laddad * @author Chris Beams * @author Dave Syer * @see org.springframework.cglib.proxy.Enhancer * @see AdvisedSupport#setProxyTargetClass * @see DefaultAopProxyFactory */
@SuppressWarnings("serial") class CglibAopProxy implements AopProxy, Serializable { // Constants for CGLIB callback array indices private static final int AOP_PROXY = 0; private static final int INVOKE_TARGET = 1; private static final int NO_OVERRIDE = 2; private static final int DISPATCH_TARGET = 3; private static final int DISPATCH_ADVISED = 4; private static final int INVOKE_EQUALS = 5; private static final int INVOKE_HASHCODE = 6;
Logger available to subclasses; static to optimize serialization.
/** Logger available to subclasses; static to optimize serialization. */
protected static final Log logger = LogFactory.getLog(CglibAopProxy.class);
Keeps track of the Classes that we have validated for final methods.
/** Keeps track of the Classes that we have validated for final methods. */
private static final Map<Class<?>, Boolean> validatedClasses = new WeakHashMap<>();
The configuration used to configure this proxy.
/** The configuration used to configure this proxy. */
protected final AdvisedSupport advised; @Nullable protected Object[] constructorArgs; @Nullable protected Class<?>[] constructorArgTypes;
Dispatcher used for methods on Advised.
/** Dispatcher used for methods on Advised. */
private final transient AdvisedDispatcher advisedDispatcher; private transient Map<Method, Integer> fixedInterceptorMap = Collections.emptyMap(); private transient int fixedInterceptorOffset;
Create a new CglibAopProxy for the given AOP configuration.
Params:
  • config – the AOP configuration as AdvisedSupport object
Throws:
  • AopConfigException – if the config is invalid. We try to throw an informative exception in this case, rather than let a mysterious failure happen later.
/** * Create a new CglibAopProxy for the given AOP configuration. * @param config the AOP configuration as AdvisedSupport object * @throws AopConfigException if the config is invalid. We try to throw an informative * exception in this case, rather than let a mysterious failure happen later. */
public CglibAopProxy(AdvisedSupport config) throws AopConfigException { Assert.notNull(config, "AdvisedSupport must not be null"); if (config.getAdvisorCount() == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) { throw new AopConfigException("No advisors and no TargetSource specified"); } this.advised = config; this.advisedDispatcher = new AdvisedDispatcher(this.advised); }
Set constructor arguments to use for creating the proxy.
Params:
  • constructorArgs – the constructor argument values
  • constructorArgTypes – the constructor argument types
/** * Set constructor arguments to use for creating the proxy. * @param constructorArgs the constructor argument values * @param constructorArgTypes the constructor argument types */
public void setConstructorArguments(@Nullable Object[] constructorArgs, @Nullable Class<?>[] constructorArgTypes) { if (constructorArgs == null || constructorArgTypes == null) { throw new IllegalArgumentException("Both 'constructorArgs' and 'constructorArgTypes' need to be specified"); } if (constructorArgs.length != constructorArgTypes.length) { throw new IllegalArgumentException("Number of 'constructorArgs' (" + constructorArgs.length + ") must match number of 'constructorArgTypes' (" + constructorArgTypes.length + ")"); } this.constructorArgs = constructorArgs; this.constructorArgTypes = constructorArgTypes; } @Override public Object getProxy() { return getProxy(null); } @Override public Object getProxy(@Nullable ClassLoader classLoader) { if (logger.isTraceEnabled()) { logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource()); } try { Class<?> rootClass = this.advised.getTargetClass(); Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy"); Class<?> proxySuperClass = rootClass; if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) { proxySuperClass = rootClass.getSuperclass(); Class<?>[] additionalInterfaces = rootClass.getInterfaces(); for (Class<?> additionalInterface : additionalInterfaces) { this.advised.addInterface(additionalInterface); } } // Validate the class, writing log messages as necessary. validateClassIfNecessary(proxySuperClass, classLoader); // Configure CGLIB Enhancer... Enhancer enhancer = createEnhancer(); if (classLoader != null) { enhancer.setClassLoader(classLoader); if (classLoader instanceof SmartClassLoader && ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) { enhancer.setUseCache(false); } } enhancer.setSuperclass(proxySuperClass); enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised)); enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE); enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader)); Callback[] callbacks = getCallbacks(rootClass); Class<?>[] types = new Class<?>[callbacks.length]; for (int x = 0; x < types.length; x++) { types[x] = callbacks[x].getClass(); } // fixedInterceptorMap only populated at this point, after getCallbacks call above enhancer.setCallbackFilter(new ProxyCallbackFilter( this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset)); enhancer.setCallbackTypes(types); // Generate the proxy class and create a proxy instance. return createProxyClassAndInstance(enhancer, callbacks); } catch (CodeGenerationException | IllegalArgumentException ex) { throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() + ": Common causes of this problem include using a final class or a non-visible class", ex); } catch (Throwable ex) { // TargetSource.getTarget() failed throw new AopConfigException("Unexpected AOP exception", ex); } } protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) { enhancer.setInterceptDuringConstruction(false); enhancer.setCallbacks(callbacks); return (this.constructorArgs != null && this.constructorArgTypes != null ? enhancer.create(this.constructorArgTypes, this.constructorArgs) : enhancer.create()); }
Creates the CGLIB Enhancer. Subclasses may wish to override this to return a custom Enhancer implementation.
/** * Creates the CGLIB {@link Enhancer}. Subclasses may wish to override this to return a custom * {@link Enhancer} implementation. */
protected Enhancer createEnhancer() { return new Enhancer(); }
Checks to see whether the supplied Class has already been validated and validates it if not.
/** * Checks to see whether the supplied {@code Class} has already been validated and * validates it if not. */
private void validateClassIfNecessary(Class<?> proxySuperClass, @Nullable ClassLoader proxyClassLoader) { if (logger.isWarnEnabled()) { synchronized (validatedClasses) { if (!validatedClasses.containsKey(proxySuperClass)) { doValidateClass(proxySuperClass, proxyClassLoader, ClassUtils.getAllInterfacesForClassAsSet(proxySuperClass)); validatedClasses.put(proxySuperClass, Boolean.TRUE); } } } }
Checks for final methods on the given Class, as well as package-visible methods across ClassLoaders, and writes warnings to the log for each one found.
/** * Checks for final methods on the given {@code Class}, as well as package-visible * methods across ClassLoaders, and writes warnings to the log for each one found. */
private void doValidateClass(Class<?> proxySuperClass, @Nullable ClassLoader proxyClassLoader, Set<Class<?>> ifcs) { if (proxySuperClass != Object.class) { Method[] methods = proxySuperClass.getDeclaredMethods(); for (Method method : methods) { int mod = method.getModifiers(); if (!Modifier.isStatic(mod) && !Modifier.isPrivate(mod)) { if (Modifier.isFinal(mod)) { if (logger.isInfoEnabled() && implementsInterface(method, ifcs)) { logger.info("Unable to proxy interface-implementing method [" + method + "] because " + "it is marked as final: Consider using interface-based JDK proxies instead!"); } if (logger.isDebugEnabled()) { logger.debug("Final method [" + method + "] cannot get proxied via CGLIB: " + "Calls to this method will NOT be routed to the target instance and " + "might lead to NPEs against uninitialized fields in the proxy instance."); } } else if (logger.isDebugEnabled() && !Modifier.isPublic(mod) && !Modifier.isProtected(mod) && proxyClassLoader != null && proxySuperClass.getClassLoader() != proxyClassLoader) { logger.debug("Method [" + method + "] is package-visible across different ClassLoaders " + "and cannot get proxied via CGLIB: Declare this method as public or protected " + "if you need to support invocations through the proxy."); } } } doValidateClass(proxySuperClass.getSuperclass(), proxyClassLoader, ifcs); } } private Callback[] getCallbacks(Class<?> rootClass) throws Exception { // Parameters used for optimization choices... boolean exposeProxy = this.advised.isExposeProxy(); boolean isFrozen = this.advised.isFrozen(); boolean isStatic = this.advised.getTargetSource().isStatic(); // Choose an "aop" interceptor (used for AOP calls). Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised); // Choose a "straight to target" interceptor. (used for calls that are // unadvised but can return this). May be required to expose the proxy. Callback targetInterceptor; if (exposeProxy) { targetInterceptor = (isStatic ? new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) : new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource())); } else { targetInterceptor = (isStatic ? new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) : new DynamicUnadvisedInterceptor(this.advised.getTargetSource())); } // Choose a "direct to target" dispatcher (used for // unadvised calls to static targets that cannot return this). Callback targetDispatcher = (isStatic ? new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp()); Callback[] mainCallbacks = new Callback[] { aopInterceptor, // for normal advice targetInterceptor, // invoke target without considering advice, if optimized new SerializableNoOp(), // no override for methods mapped to this targetDispatcher, this.advisedDispatcher, new EqualsInterceptor(this.advised), new HashCodeInterceptor(this.advised) }; Callback[] callbacks; // If the target is a static one and the advice chain is frozen, // then we can make some optimizations by sending the AOP calls // direct to the target using the fixed chain for that method. if (isStatic && isFrozen) { Method[] methods = rootClass.getMethods(); Callback[] fixedCallbacks = new Callback[methods.length]; this.fixedInterceptorMap = CollectionUtils.newHashMap(methods.length); // TODO: small memory optimization here (can skip creation for methods with no advice) for (int x = 0; x < methods.length; x++) { Method method = methods[x]; List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, rootClass); fixedCallbacks[x] = new FixedChainStaticTargetInterceptor( chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass()); this.fixedInterceptorMap.put(method, x); } // Now copy both the callbacks from mainCallbacks // and fixedCallbacks into the callbacks array. callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length]; System.arraycopy(mainCallbacks, 0, callbacks, 0, mainCallbacks.length); System.arraycopy(fixedCallbacks, 0, callbacks, mainCallbacks.length, fixedCallbacks.length); this.fixedInterceptorOffset = mainCallbacks.length; } else { callbacks = mainCallbacks; } return callbacks; } @Override public boolean equals(@Nullable Object other) { return (this == other || (other instanceof CglibAopProxy && AopProxyUtils.equalsInProxy(this.advised, ((CglibAopProxy) other).advised))); } @Override public int hashCode() { return CglibAopProxy.class.hashCode() * 13 + this.advised.getTargetSource().hashCode(); }
Check whether the given method is declared on any of the given interfaces.
/** * Check whether the given method is declared on any of the given interfaces. */
private static boolean implementsInterface(Method method, Set<Class<?>> ifcs) { for (Class<?> ifc : ifcs) { if (ClassUtils.hasMethod(ifc, method)) { return true; } } return false; }
Process a return value. Wraps a return of this if necessary to be the proxy and also verifies that null is not returned as a primitive.
/** * Process a return value. Wraps a return of {@code this} if necessary to be the * {@code proxy} and also verifies that {@code null} is not returned as a primitive. */
@Nullable private static Object processReturnType( Object proxy, @Nullable Object target, Method method, @Nullable Object returnValue) { // Massage return value if necessary if (returnValue != null && returnValue == target && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) { // Special case: it returned "this". Note that we can't help // if the target sets a reference to itself in another returned object. returnValue = proxy; } Class<?> returnType = method.getReturnType(); if (returnValue == null && returnType != Void.TYPE && returnType.isPrimitive()) { throw new AopInvocationException( "Null return value from advice does not match primitive return type for: " + method); } return returnValue; }
Serializable replacement for CGLIB's NoOp interface. Public to allow use elsewhere in the framework.
/** * Serializable replacement for CGLIB's NoOp interface. * Public to allow use elsewhere in the framework. */
public static class SerializableNoOp implements NoOp, Serializable { }
Method interceptor used for static targets with no advice chain. The call is passed directly back to the target. Used when the proxy needs to be exposed and it can't be determined that the method won't return this.
/** * Method interceptor used for static targets with no advice chain. The call * is passed directly back to the target. Used when the proxy needs to be * exposed and it can't be determined that the method won't return * {@code this}. */
private static class StaticUnadvisedInterceptor implements MethodInterceptor, Serializable { @Nullable private final Object target; public StaticUnadvisedInterceptor(@Nullable Object target) { this.target = target; } @Override @Nullable public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object retVal = methodProxy.invoke(this.target, args); return processReturnType(proxy, this.target, method, retVal); } }
Method interceptor used for static targets with no advice chain, when the proxy is to be exposed.
/** * Method interceptor used for static targets with no advice chain, when the * proxy is to be exposed. */
private static class StaticUnadvisedExposedInterceptor implements MethodInterceptor, Serializable { @Nullable private final Object target; public StaticUnadvisedExposedInterceptor(@Nullable Object target) { this.target = target; } @Override @Nullable public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object oldProxy = null; try { oldProxy = AopContext.setCurrentProxy(proxy); Object retVal = methodProxy.invoke(this.target, args); return processReturnType(proxy, this.target, method, retVal); } finally { AopContext.setCurrentProxy(oldProxy); } } }
Interceptor used to invoke a dynamic target without creating a method invocation or evaluating an advice chain. (We know there was no advice for this method.)
/** * Interceptor used to invoke a dynamic target without creating a method * invocation or evaluating an advice chain. (We know there was no advice * for this method.) */
private static class DynamicUnadvisedInterceptor implements MethodInterceptor, Serializable { private final TargetSource targetSource; public DynamicUnadvisedInterceptor(TargetSource targetSource) { this.targetSource = targetSource; } @Override @Nullable public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object target = this.targetSource.getTarget(); try { Object retVal = methodProxy.invoke(target, args); return processReturnType(proxy, target, method, retVal); } finally { if (target != null) { this.targetSource.releaseTarget(target); } } } }
Interceptor for unadvised dynamic targets when the proxy needs exposing.
/** * Interceptor for unadvised dynamic targets when the proxy needs exposing. */
private static class DynamicUnadvisedExposedInterceptor implements MethodInterceptor, Serializable { private final TargetSource targetSource; public DynamicUnadvisedExposedInterceptor(TargetSource targetSource) { this.targetSource = targetSource; } @Override @Nullable public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object oldProxy = null; Object target = this.targetSource.getTarget(); try { oldProxy = AopContext.setCurrentProxy(proxy); Object retVal = methodProxy.invoke(target, args); return processReturnType(proxy, target, method, retVal); } finally { AopContext.setCurrentProxy(oldProxy); if (target != null) { this.targetSource.releaseTarget(target); } } } }
Dispatcher for a static target. Dispatcher is much faster than interceptor. This will be used whenever it can be determined that a method definitely does not return "this"
/** * Dispatcher for a static target. Dispatcher is much faster than * interceptor. This will be used whenever it can be determined that a * method definitely does not return "this" */
private static class StaticDispatcher implements Dispatcher, Serializable { @Nullable private final Object target; public StaticDispatcher(@Nullable Object target) { this.target = target; } @Override @Nullable public Object loadObject() { return this.target; } }
Dispatcher for any methods declared on the Advised class.
/** * Dispatcher for any methods declared on the Advised class. */
private static class AdvisedDispatcher implements Dispatcher, Serializable { private final AdvisedSupport advised; public AdvisedDispatcher(AdvisedSupport advised) { this.advised = advised; } @Override public Object loadObject() { return this.advised; } }
Dispatcher for the equals method. Ensures that the method call is always handled by this class.
/** * Dispatcher for the {@code equals} method. * Ensures that the method call is always handled by this class. */
private static class EqualsInterceptor implements MethodInterceptor, Serializable { private final AdvisedSupport advised; public EqualsInterceptor(AdvisedSupport advised) { this.advised = advised; } @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) { Object other = args[0]; if (proxy == other) { return true; } if (other instanceof Factory) { Callback callback = ((Factory) other).getCallback(INVOKE_EQUALS); if (!(callback instanceof EqualsInterceptor)) { return false; } AdvisedSupport otherAdvised = ((EqualsInterceptor) callback).advised; return AopProxyUtils.equalsInProxy(this.advised, otherAdvised); } else { return false; } } }
Dispatcher for the hashCode method. Ensures that the method call is always handled by this class.
/** * Dispatcher for the {@code hashCode} method. * Ensures that the method call is always handled by this class. */
private static class HashCodeInterceptor implements MethodInterceptor, Serializable { private final AdvisedSupport advised; public HashCodeInterceptor(AdvisedSupport advised) { this.advised = advised; } @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) { return CglibAopProxy.class.hashCode() * 13 + this.advised.getTargetSource().hashCode(); } }
Interceptor used specifically for advised methods on a frozen, static proxy.
/** * Interceptor used specifically for advised methods on a frozen, static proxy. */
private static class FixedChainStaticTargetInterceptor implements MethodInterceptor, Serializable { private final List<Object> adviceChain; @Nullable private final Object target; @Nullable private final Class<?> targetClass; public FixedChainStaticTargetInterceptor( List<Object> adviceChain, @Nullable Object target, @Nullable Class<?> targetClass) { this.adviceChain = adviceChain; this.target = target; this.targetClass = targetClass; } @Override @Nullable public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { MethodInvocation invocation = new CglibMethodInvocation( proxy, this.target, method, args, this.targetClass, this.adviceChain, methodProxy); // If we get here, we need to create a MethodInvocation. Object retVal = invocation.proceed(); retVal = processReturnType(proxy, this.target, method, retVal); return retVal; } }
General purpose AOP callback. Used when the target is dynamic or when the proxy is not frozen.
/** * General purpose AOP callback. Used when the target is dynamic or when the * proxy is not frozen. */
private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable { private final AdvisedSupport advised; public DynamicAdvisedInterceptor(AdvisedSupport advised) { this.advised = advised; } @Override @Nullable public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object oldProxy = null; boolean setProxyContext = false; Object target = null; TargetSource targetSource = this.advised.getTargetSource(); try { if (this.advised.exposeProxy) { // Make invocation available if necessary. oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } // Get as late as possible to minimize the time we "own" the target, in case it comes from a pool... target = targetSource.getTarget(); Class<?> targetClass = (target != null ? target.getClass() : null); List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); Object retVal; // Check whether we only have one InvokerInterceptor: that is, // no real advice, but just reflective invocation of the target. if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) { // We can skip creating a MethodInvocation: just invoke the target directly. // Note that the final invoker must be an InvokerInterceptor, so we know // it does nothing but a reflective operation on the target, and no hot // swapping or fancy proxying. Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); retVal = methodProxy.invoke(target, argsToUse); } else { // We need to create a method invocation... retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed(); } retVal = processReturnType(proxy, target, method, retVal); return retVal; } finally { if (target != null && !targetSource.isStatic()) { targetSource.releaseTarget(target); } if (setProxyContext) { // Restore old proxy. AopContext.setCurrentProxy(oldProxy); } } } @Override public boolean equals(@Nullable Object other) { return (this == other || (other instanceof DynamicAdvisedInterceptor && this.advised.equals(((DynamicAdvisedInterceptor) other).advised))); }
CGLIB uses this to drive proxy creation.
/** * CGLIB uses this to drive proxy creation. */
@Override public int hashCode() { return this.advised.hashCode(); } }
Implementation of AOP Alliance MethodInvocation used by this AOP proxy.
/** * Implementation of AOP Alliance MethodInvocation used by this AOP proxy. */
private static class CglibMethodInvocation extends ReflectiveMethodInvocation { @Nullable private final MethodProxy methodProxy; public CglibMethodInvocation(Object proxy, @Nullable Object target, Method method, Object[] arguments, @Nullable Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) { super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers); // Only use method proxy for public methods not derived from java.lang.Object this.methodProxy = (Modifier.isPublic(method.getModifiers()) && method.getDeclaringClass() != Object.class && !AopUtils.isEqualsMethod(method) && !AopUtils.isHashCodeMethod(method) && !AopUtils.isToStringMethod(method) ? methodProxy : null); } @Override @Nullable public Object proceed() throws Throwable { try { return super.proceed(); } catch (RuntimeException ex) { throw ex; } catch (Exception ex) { if (ReflectionUtils.declaresException(getMethod(), ex.getClass()) || KotlinDetector.isKotlinType(getMethod().getDeclaringClass())) { // Propagate original exception if declared on the target method // (with callers expecting it). Always propagate it for Kotlin code // since checked exceptions do not have to be explicitly declared there. throw ex; } else { // Checked exception thrown in the interceptor but not declared on the // target method signature -> apply an UndeclaredThrowableException, // aligned with standard JDK dynamic proxy behavior. throw new UndeclaredThrowableException(ex); } } }
Gives a marginal performance improvement versus using reflection to invoke the target when invoking public methods.
/** * Gives a marginal performance improvement versus using reflection to * invoke the target when invoking public methods. */
@Override protected Object invokeJoinpoint() throws Throwable { if (this.methodProxy != null) { return this.methodProxy.invoke(this.target, this.arguments); } else { return super.invokeJoinpoint(); } } }
CallbackFilter to assign Callbacks to methods.
/** * CallbackFilter to assign Callbacks to methods. */
private static class ProxyCallbackFilter implements CallbackFilter { private final AdvisedSupport advised; private final Map<Method, Integer> fixedInterceptorMap; private final int fixedInterceptorOffset; public ProxyCallbackFilter( AdvisedSupport advised, Map<Method, Integer> fixedInterceptorMap, int fixedInterceptorOffset) { this.advised = advised; this.fixedInterceptorMap = fixedInterceptorMap; this.fixedInterceptorOffset = fixedInterceptorOffset; }
Implementation of CallbackFilter.accept() to return the index of the callback we need.

The callbacks for each proxy are built up of a set of fixed callbacks for general use and then a set of callbacks that are specific to a method for use on static targets with a fixed advice chain.

The callback used is determined thus:

For exposed proxies
Exposing the proxy requires code to execute before and after the method/chain invocation. This means we must use DynamicAdvisedInterceptor, since all other interceptors can avoid the need for a try/catch block
For Object.finalize():
No override for this method is used.
For equals():
The EqualsInterceptor is used to redirect equals() calls to a special handler to this proxy.
For methods on the Advised class:
the AdvisedDispatcher is used to dispatch the call directly to the target
For advised methods:
If the target is static and the advice chain is frozen then a FixedChainStaticTargetInterceptor specific to the method is used to invoke the advice chain. Otherwise a DynamicAdvisedInterceptor is used.
For non-advised methods:
Where it can be determined that the method will not return this or when ProxyFactory.getExposeProxy() returns false, then a Dispatcher is used. For static targets, the StaticDispatcher is used; and for dynamic targets, a DynamicUnadvisedInterceptor is used. If it possible for the method to return this then a StaticUnadvisedInterceptor is used for static targets - the DynamicUnadvisedInterceptor already considers this.
/** * Implementation of CallbackFilter.accept() to return the index of the * callback we need. * <p>The callbacks for each proxy are built up of a set of fixed callbacks * for general use and then a set of callbacks that are specific to a method * for use on static targets with a fixed advice chain. * <p>The callback used is determined thus: * <dl> * <dt>For exposed proxies</dt> * <dd>Exposing the proxy requires code to execute before and after the * method/chain invocation. This means we must use * DynamicAdvisedInterceptor, since all other interceptors can avoid the * need for a try/catch block</dd> * <dt>For Object.finalize():</dt> * <dd>No override for this method is used.</dd> * <dt>For equals():</dt> * <dd>The EqualsInterceptor is used to redirect equals() calls to a * special handler to this proxy.</dd> * <dt>For methods on the Advised class:</dt> * <dd>the AdvisedDispatcher is used to dispatch the call directly to * the target</dd> * <dt>For advised methods:</dt> * <dd>If the target is static and the advice chain is frozen then a * FixedChainStaticTargetInterceptor specific to the method is used to * invoke the advice chain. Otherwise a DynamicAdvisedInterceptor is * used.</dd> * <dt>For non-advised methods:</dt> * <dd>Where it can be determined that the method will not return {@code this} * or when {@code ProxyFactory.getExposeProxy()} returns {@code false}, * then a Dispatcher is used. For static targets, the StaticDispatcher is used; * and for dynamic targets, a DynamicUnadvisedInterceptor is used. * If it possible for the method to return {@code this} then a * StaticUnadvisedInterceptor is used for static targets - the * DynamicUnadvisedInterceptor already considers this.</dd> * </dl> */
@Override public int accept(Method method) { if (AopUtils.isFinalizeMethod(method)) { logger.trace("Found finalize() method - using NO_OVERRIDE"); return NO_OVERRIDE; } if (!this.advised.isOpaque() && method.getDeclaringClass().isInterface() && method.getDeclaringClass().isAssignableFrom(Advised.class)) { if (logger.isTraceEnabled()) { logger.trace("Method is declared on Advised interface: " + method); } return DISPATCH_ADVISED; } // We must always proxy equals, to direct calls to this. if (AopUtils.isEqualsMethod(method)) { if (logger.isTraceEnabled()) { logger.trace("Found 'equals' method: " + method); } return INVOKE_EQUALS; } // We must always calculate hashCode based on the proxy. if (AopUtils.isHashCodeMethod(method)) { if (logger.isTraceEnabled()) { logger.trace("Found 'hashCode' method: " + method); } return INVOKE_HASHCODE; } Class<?> targetClass = this.advised.getTargetClass(); // Proxy is not yet available, but that shouldn't matter. List<?> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); boolean haveAdvice = !chain.isEmpty(); boolean exposeProxy = this.advised.isExposeProxy(); boolean isStatic = this.advised.getTargetSource().isStatic(); boolean isFrozen = this.advised.isFrozen(); if (haveAdvice || !isFrozen) { // If exposing the proxy, then AOP_PROXY must be used. if (exposeProxy) { if (logger.isTraceEnabled()) { logger.trace("Must expose proxy on advised method: " + method); } return AOP_PROXY; } // Check to see if we have fixed interceptor to serve this method. // Else use the AOP_PROXY. if (isStatic && isFrozen && this.fixedInterceptorMap.containsKey(method)) { if (logger.isTraceEnabled()) { logger.trace("Method has advice and optimizations are enabled: " + method); } // We know that we are optimizing so we can use the FixedStaticChainInterceptors. int index = this.fixedInterceptorMap.get(method); return (index + this.fixedInterceptorOffset); } else { if (logger.isTraceEnabled()) { logger.trace("Unable to apply any optimizations to advised method: " + method); } return AOP_PROXY; } } else { // See if the return type of the method is outside the class hierarchy of the target type. // If so we know it never needs to have return type massage and can use a dispatcher. // If the proxy is being exposed, then must use the interceptor the correct one is already // configured. If the target is not static, then we cannot use a dispatcher because the // target needs to be explicitly released after the invocation. if (exposeProxy || !isStatic) { return INVOKE_TARGET; } Class<?> returnType = method.getReturnType(); if (targetClass != null && returnType.isAssignableFrom(targetClass)) { if (logger.isTraceEnabled()) { logger.trace("Method return type is assignable from target type and " + "may therefore return 'this' - using INVOKE_TARGET: " + method); } return INVOKE_TARGET; } else { if (logger.isTraceEnabled()) { logger.trace("Method return type ensures 'this' cannot be returned - " + "using DISPATCH_TARGET: " + method); } return DISPATCH_TARGET; } } } @Override public boolean equals(@Nullable Object other) { if (this == other) { return true; } if (!(other instanceof ProxyCallbackFilter)) { return false; } ProxyCallbackFilter otherCallbackFilter = (ProxyCallbackFilter) other; AdvisedSupport otherAdvised = otherCallbackFilter.advised; if (this.advised.isFrozen() != otherAdvised.isFrozen()) { return false; } if (this.advised.isExposeProxy() != otherAdvised.isExposeProxy()) { return false; } if (this.advised.getTargetSource().isStatic() != otherAdvised.getTargetSource().isStatic()) { return false; } if (!AopProxyUtils.equalsProxiedInterfaces(this.advised, otherAdvised)) { return false; } // Advice instance identity is unimportant to the proxy class: // All that matters is type and ordering. if (this.advised.getAdvisorCount() != otherAdvised.getAdvisorCount()) { return false; } Advisor[] thisAdvisors = this.advised.getAdvisors(); Advisor[] thatAdvisors = otherAdvised.getAdvisors(); for (int i = 0; i < thisAdvisors.length; i++) { Advisor thisAdvisor = thisAdvisors[i]; Advisor thatAdvisor = thatAdvisors[i]; if (!equalsAdviceClasses(thisAdvisor, thatAdvisor)) { return false; } if (!equalsPointcuts(thisAdvisor, thatAdvisor)) { return false; } } return true; } private static boolean equalsAdviceClasses(Advisor a, Advisor b) { return (a.getAdvice().getClass() == b.getAdvice().getClass()); } private static boolean equalsPointcuts(Advisor a, Advisor b) { // If only one of the advisor (but not both) is PointcutAdvisor, then it is a mismatch. // Takes care of the situations where an IntroductionAdvisor is used (see SPR-3959). return (!(a instanceof PointcutAdvisor) || (b instanceof PointcutAdvisor && ObjectUtils.nullSafeEquals(((PointcutAdvisor) a).getPointcut(), ((PointcutAdvisor) b).getPointcut()))); } @Override public int hashCode() { int hashCode = 0; Advisor[] advisors = this.advised.getAdvisors(); for (Advisor advisor : advisors) { Advice advice = advisor.getAdvice(); hashCode = 13 * hashCode + advice.getClass().hashCode(); } hashCode = 13 * hashCode + (this.advised.isFrozen() ? 1 : 0); hashCode = 13 * hashCode + (this.advised.isExposeProxy() ? 1 : 0); hashCode = 13 * hashCode + (this.advised.isOptimize() ? 1 : 0); hashCode = 13 * hashCode + (this.advised.isOpaque() ? 1 : 0); return hashCode; } } }