Open Source Repository

Home /spring/spring-aop-3.0.5 | Repository Home


org/springframework/aop/framework/JdkDynamicAopProxy.java
/*
 * Copyright 2002-2010 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
 *
 *      http://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.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;

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

import org.springframework.aop.RawTargetAccess;
import org.springframework.aop.TargetSource;
import org.springframework.aop.support.AopUtils;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

/**
 * JDK-based {@link AopProxy} implementation for the Spring AOP framework,
 * based on JDK {@link java.lang.reflect.Proxy dynamic proxies}.
 *
 <p>Creates a dynamic proxy, implementing the interfaces exposed by
 * the AopProxy. Dynamic proxies <i>cannot</i> be used to proxy methods
 * defined in classes, rather than interfaces.
 *
 <p>Objects of this type should be obtained through proxy factories,
 * configured by an {@link AdvisedSupport} class. This class is internal
 * to Spring's AOP framework and need not be used directly by client code.
 *
 <p>Proxies created using this class will be thread-safe if the
 * underlying (target) class is thread-safe.
 *
 <p>Proxies are serializable so long as all Advisors (including Advices
 * and Pointcuts) and the TargetSource are serializable.
 *
 @author Rod Johnson
 @author Juergen Hoeller
 @author Rob Harrop
 @see java.lang.reflect.Proxy
 @see AdvisedSupport
 @see ProxyFactory
 */
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {

  /** use serialVersionUID from Spring 1.2 for interoperability */
  private static final long serialVersionUID = 5531744639992436476L;


  /*
   * NOTE: We could avoid the code duplication between this class and the CGLIB
   * proxies by refactoring "invoke" into a template method. However, this approach
   * adds at least 10% performance overhead versus a copy-paste solution, so we sacrifice
   * elegance for performance. (We have a good test suite to ensure that the different
   * proxies behave the same :-)
   * This way, we can also more easily take advantage of minor optimizations in each class.
   */

  /** We use a static Log to avoid serialization issues */
  private static Log logger = LogFactory.getLog(JdkDynamicAopProxy.class);

  /** Config used to configure this proxy */
  private final AdvisedSupport advised;

  /**
   * Is the {@link #equals} method defined on the proxied interfaces?
   */
  private boolean equalsDefined;

  /**
   * Is the {@link #hashCode} method defined on the proxied interfaces?
   */
  private boolean hashCodeDefined;


  /**
   * Construct a new JdkDynamicAopProxy 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 JdkDynamicAopProxy(AdvisedSupport configthrows AopConfigException {
    Assert.notNull(config, "AdvisedSupport must not be null");
    if (config.getAdvisors().length == && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
      throw new AopConfigException("No advisors and no TargetSource specified");
    }
    this.advised = config;
  }


  public Object getProxy() {
    return getProxy(ClassUtils.getDefaultClassLoader());
  }

  public Object getProxy(ClassLoader classLoader) {
    if (logger.isDebugEnabled()) {
      logger.debug("Creating JDK dynamic proxy: target source is " this.advised.getTargetSource());
    }
    Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
    findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
  }

  /**
   * Finds any {@link #equals} or {@link #hashCode} method that may be defined
   * on the supplied set of interfaces.
   @param proxiedInterfaces the interfaces to introspect
   */
  private void findDefinedEqualsAndHashCodeMethods(Class[] proxiedInterfaces) {
    for (Class proxiedInterface : proxiedInterfaces) {
      Method[] methods = proxiedInterface.getDeclaredMethods();
      for (Method method : methods) {
        if (AopUtils.isEqualsMethod(method)) {
          this.equalsDefined = true;
        }
        if (AopUtils.isHashCodeMethod(method)) {
          this.hashCodeDefined = true;
        }
        if (this.equalsDefined && this.hashCodeDefined) {
          return;
        }
      }
    }
  }


  /**
   * Implementation of <code>InvocationHandler.invoke</code>.
   <p>Callers will see exactly the exception thrown by the target,
   * unless a hook method throws an exception.
   */
  public Object invoke(Object proxy, Method method, Object[] argsthrows Throwable {
    MethodInvocation invocation;
    Object oldProxy = null;
    boolean setProxyContext = false;

    TargetSource targetSource = this.advised.targetSource;
    Class targetClass = null;
    Object target = null;

    try {
      if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
        // The target does not implement the equals(Object) method itself.
        return equals(args[0]);
      }
      if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
        // The target does not implement the hashCode() method itself.
        return hashCode();
      }
      if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
          method.getDeclaringClass().isAssignableFrom(Advised.class)) {
        // Service invocations on ProxyConfig with the proxy config...
        return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
      }

      Object retVal;

      if (this.advised.exposeProxy) {
        // Make invocation available if necessary.
        oldProxy = AopContext.setCurrentProxy(proxy);
        setProxyContext = true;
      }

      // May be null. Get as late as possible to minimize the time we "own" the target,
      // in case it comes from a pool.
      target = targetSource.getTarget();
      if (target != null) {
        targetClass = target.getClass();
      }

      // Get the interception chain for this method.
      List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

      // Check whether we have any advice. If we don't, we can fallback on direct
      // reflective invocation of the target, and avoid creating a MethodInvocation.
      if (chain.isEmpty()) {
        // 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.
        retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
      }
      else {
        // We need to create a method invocation...
        invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
        // Proceed to the joinpoint through the interceptor chain.
        retVal = invocation.proceed();
      }

      // Massage return value if necessary.
      if (retVal != null && retVal == target && method.getReturnType().isInstance(proxy&&
          !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
        // Special case: it returned "this" and the return type of the method
        // is type-compatible. Note that we can't help if the target sets
        // a reference to itself in another returned object.
        retVal = proxy;
      }
      return retVal;
    }
    finally {
      if (target != null && !targetSource.isStatic()) {
        // Must have come from TargetSource.
        targetSource.releaseTarget(target);
      }
      if (setProxyContext) {
        // Restore old proxy.
        AopContext.setCurrentProxy(oldProxy);
      }
    }
  }


  /**
   * Equality means interfaces, advisors and TargetSource are equal.
   <p>The compared object may be a JdkDynamicAopProxy instance itself
   * or a dynamic proxy wrapping a JdkDynamicAopProxy instance.
   */
  @Override
  public boolean equals(Object other) {
    if (other == this) {
      return true;
    }
    if (other == null) {
      return false;
    }

    JdkDynamicAopProxy otherProxy;
    if (other instanceof JdkDynamicAopProxy) {
      otherProxy = (JdkDynamicAopProxyother;
    }
    else if (Proxy.isProxyClass(other.getClass())) {
      InvocationHandler ih = Proxy.getInvocationHandler(other);
      if (!(ih instanceof JdkDynamicAopProxy)) {
        return false;
      }
      otherProxy = (JdkDynamicAopProxyih;
    }
    else {
      // Not a valid comparison...
      return false;
    }

    // If we get here, otherProxy is the other AopProxy.
    return AopProxyUtils.equalsInProxy(this.advised, otherProxy.advised);
  }

  /**
   * Proxy uses the hash code of the TargetSource.
   */
  @Override
  public int hashCode() {
    return JdkDynamicAopProxy.class.hashCode() 13 this.advised.getTargetSource().hashCode();
  }

}