Save This Page
Home » Spring-Framework-090522 » org.springframework » aop » framework » [javadoc | source]
    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   }

Save This Page
Home » Spring-Framework-090522 » org.springframework » aop » framework » [javadoc | source]