Save This Page
Home » spring-framework-2.5.6-with-dependencies » org.springframework » aop » framework » [javadoc | source]
    1   /*
    2    * Copyright 2002-2007 the original author or authors.
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    *      http://www.apache.org/licenses/LICENSE-2.0
    9    *
   10    * Unless required by applicable law or agreed to in writing, software
   11    * distributed under the License is distributed on an "AS IS" BASIS,
   12    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   13    * See the License for the specific language governing permissions and
   14    * limitations under the License.
   15    */
   16   
   17   package org.springframework.aop.framework;
   18   
   19   import java.io.Serializable;
   20   import java.lang.reflect.InvocationHandler;
   21   import java.lang.reflect.Method;
   22   import java.lang.reflect.Proxy;
   23   import java.util.List;
   24   
   25   import org.aopalliance.intercept.MethodInvocation;
   26   import org.apache.commons.logging.Log;
   27   import org.apache.commons.logging.LogFactory;
   28   
   29   import org.springframework.aop.RawTargetAccess;
   30   import org.springframework.aop.TargetSource;
   31   import org.springframework.aop.support.AopUtils;
   32   import org.springframework.util.Assert;
   33   import org.springframework.util.ClassUtils;
   34   
   35   /**
   36    * JDK-based {@link AopProxy} implementation for the Spring AOP framework,
   37    * based on JDK {@link java.lang.reflect.Proxy dynamic proxies}.
   38    *
   39    * <p>Creates a dynamic proxy, implementing the interfaces exposed by
   40    * the AopProxy. Dynamic proxies <i>cannot</i> be used to proxy methods
   41    * defined in classes, rather than interfaces.
   42    *
   43    * <p>Objects of this type should be obtained through proxy factories,
   44    * configured by an {@link AdvisedSupport} class. This class is internal
   45    * to Spring's AOP framework and need not be used directly by client code.
   46    *
   47    * <p>Proxies created using this class will be thread-safe if the
   48    * underlying (target) class is thread-safe.
   49    *
   50    * <p>Proxies are serializable so long as all Advisors (including Advices
   51    * and Pointcuts) and the TargetSource are serializable.
   52    *
   53    * @author Rod Johnson
   54    * @author Juergen Hoeller
   55    * @author Rob Harrop
   56    * @see java.lang.reflect.Proxy
   57    * @see AdvisedSupport
   58    * @see ProxyFactory
   59    */
   60   final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
   61   
   62   	/** use serialVersionUID from Spring 1.2 for interoperability */
   63   	private static final long serialVersionUID = 5531744639992436476L;
   64   
   65   
   66   	/*
   67   	 * NOTE: We could avoid the code duplication between this class and the CGLIB
   68   	 * proxies by refactoring "invoke" into a template method. However, this approach
   69   	 * adds at least 10% performance overhead versus a copy-paste solution, so we sacrifice
   70   	 * elegance for performance. (We have a good test suite to ensure that the different
   71   	 * proxies behave the same :-)
   72   	 * This way, we can also more easily take advantage of minor optimizations in each class.
   73   	 */
   74   
   75   	/** We use a static Log to avoid serialization issues */
   76   	private static Log logger = LogFactory.getLog(JdkDynamicAopProxy.class);
   77   
   78   	/** Config used to configure this proxy */
   79   	private final AdvisedSupport advised;
   80   
   81   	/**
   82   	 * Is the {@link #equals} method defined on the proxied interfaces?
   83   	 */
   84   	private boolean equalsDefined;
   85   
   86   	/**
   87   	 * Is the {@link #hashCode} method defined on the proxied interfaces?
   88   	 */
   89   	private boolean hashCodeDefined;
   90   
   91   
   92   	/**
   93   	 * Construct a new JdkDynamicAopProxy for the given AOP configuration.
   94   	 * @param config the AOP configuration as AdvisedSupport object
   95   	 * @throws AopConfigException if the config is invalid. We try to throw an informative
   96   	 * exception in this case, rather than let a mysterious failure happen later.
   97   	 */
   98   	public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
   99   		Assert.notNull(config, "AdvisedSupport must not be null");
  100   		if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
  101   			throw new AopConfigException("No advisors and no TargetSource specified");
  102   		}
  103   		this.advised = config;
  104   	}
  105   
  106   
  107   	public Object getProxy() {
  108   		return getProxy(ClassUtils.getDefaultClassLoader());
  109   	}
  110   
  111   	public Object getProxy(ClassLoader classLoader) {
  112   		if (logger.isDebugEnabled()) {
  113   			logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
  114   		}
  115   		Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
  116   		findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
  117   		return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
  118   	}
  119   
  120   	/**
  121   	 * Finds any {@link #equals} or {@link #hashCode} method that may be defined
  122   	 * on the supplied set of interfaces.
  123   	 * @param proxiedInterfaces the interfaces to introspect
  124   	 */
  125   	private void findDefinedEqualsAndHashCodeMethods(Class[] proxiedInterfaces) {
  126   		for (int i = 0; i < proxiedInterfaces.length; i++) {
  127   			Class proxiedInterface = proxiedInterfaces[i];
  128   			Method[] methods = proxiedInterface.getDeclaredMethods();
  129   			for (int j = 0; j < methods.length; j++) {
  130   				Method method = methods[j];
  131   				if (AopUtils.isEqualsMethod(method)) {
  132   					this.equalsDefined = true;
  133   				}
  134   				if (AopUtils.isHashCodeMethod(method)) {
  135   					this.hashCodeDefined = true;
  136   				}
  137   				if (this.equalsDefined && this.hashCodeDefined) {
  138   					return;
  139   				}
  140   			}
  141   		}
  142   	}
  143   
  144   
  145   	/**
  146   	 * Implementation of <code>InvocationHandler.invoke</code>.
  147   	 * <p>Callers will see exactly the exception thrown by the target,
  148   	 * unless a hook method throws an exception.
  149   	 */
  150   	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  151   		MethodInvocation invocation = null;
  152   		Object oldProxy = null;
  153   		boolean setProxyContext = false;
  154   
  155   		TargetSource targetSource = this.advised.targetSource;
  156   		Class targetClass = null;
  157   		Object target = null;
  158   
  159   		try {
  160   			if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
  161   				// The target does not implement the equals(Object) method itself.
  162   				return (equals(args[0]) ? Boolean.TRUE : Boolean.FALSE);
  163   			}
  164   			if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
  165   				// The target does not implement the hashCode() method itself.
  166   				return new Integer(hashCode());
  167   			}
  168   			if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
  169   					method.getDeclaringClass().isAssignableFrom(Advised.class)) {
  170   				// Service invocations on ProxyConfig with the proxy config...
  171   				return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
  172   			}
  173   
  174   			Object retVal = null;
  175   
  176   			if (this.advised.exposeProxy) {
  177   				// Make invocation available if necessary.
  178   				oldProxy = AopContext.setCurrentProxy(proxy);
  179   				setProxyContext = true;
  180   			}
  181   
  182   			// May be <code>null</code>. Get as late as possible to minimize the time we "own" the target,
  183   			// in case it comes from a pool.
  184   			target = targetSource.getTarget();
  185   			if (target != null) {
  186   				targetClass = target.getClass();
  187   			}
  188   
  189   			// Get the interception chain for this method.
  190   			List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
  191   
  192   			// Check whether we have any advice. If we don't, we can fallback on direct
  193   			// reflective invocation of the target, and avoid creating a MethodInvocation.
  194   			if (chain.isEmpty()) {
  195   				// We can skip creating a MethodInvocation: just invoke the target directly
  196   				// Note that the final invoker must be an InvokerInterceptor so we know it does
  197   				// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
  198   				retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
  199   			}
  200   			else {
  201   				// We need to create a method invocation...
  202   				invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
  203   				// Proceed to the joinpoint through the interceptor chain.
  204   				retVal = invocation.proceed();
  205   			}
  206   
  207   			// Massage return value if necessary.
  208   			if (retVal != null && retVal == target && method.getReturnType().isInstance(proxy) &&
  209   					!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
  210   				// Special case: it returned "this" and the return type of the method
  211   				// is type-compatible. Note that we can't help if the target sets
  212   				// a reference to itself in another returned object.
  213   				retVal = proxy;
  214   			}
  215   			return retVal;
  216   		}
  217   		finally {
  218   			if (target != null && !targetSource.isStatic()) {
  219   				// Must have come from TargetSource.
  220   				targetSource.releaseTarget(target);
  221   			}
  222   			if (setProxyContext) {
  223   				// Restore old proxy.
  224   				AopContext.setCurrentProxy(oldProxy);
  225   			}
  226   		}
  227   	}
  228   
  229   
  230   	/**
  231   	 * Equality means interfaces, advisors and TargetSource are equal.
  232   	 * <p>The compared object may be a JdkDynamicAopProxy instance itself
  233   	 * or a dynamic proxy wrapping a JdkDynamicAopProxy instance.
  234   	 */
  235   	public boolean equals(Object other) {
  236   		if (other == this) {
  237   			return true;
  238   		}
  239   		if (other == null) {
  240   			return false;
  241   		}
  242   
  243   		JdkDynamicAopProxy otherProxy = null;
  244   		if (other instanceof JdkDynamicAopProxy) {
  245   			otherProxy = (JdkDynamicAopProxy) other;
  246   		}
  247   		else if (Proxy.isProxyClass(other.getClass())) {
  248   			InvocationHandler ih = Proxy.getInvocationHandler(other);
  249   			if (!(ih instanceof JdkDynamicAopProxy)) {
  250   				return false;
  251   			}
  252   			otherProxy = (JdkDynamicAopProxy) ih;
  253   		}
  254   		else {
  255   			// Not a valid comparison...
  256   			return false;
  257   		}
  258   
  259   		// If we get here, aopr2 is the other AopProxy.
  260   		return AopProxyUtils.equalsInProxy(this.advised, otherProxy.advised);
  261   	}
  262   
  263   	/**
  264   	 * Proxy uses the hash code of the TargetSource.
  265   	 */
  266   	public int hashCode() {
  267   		return JdkDynamicAopProxy.class.hashCode() * 13 + this.advised.getTargetSource().hashCode();
  268   	}
  269   
  270   }

Save This Page
Home » spring-framework-2.5.6-with-dependencies » org.springframework » aop » framework » [javadoc | source]