/**
  * 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 (int i = 0; i < proxiedInterfaces.length; i++) {
     Class proxiedInterface = proxiedInterfaces[i];
     Method[] methods = proxiedInterface.getDeclaredMethods();
     for (int j = 0; j < methods.length; j++) {
       Method method = methods[j];
       if (AopUtils.isEqualsMethod(method)) {
         this.equalsDefined = true;
       }
       if (AopUtils.isHashCodeMethod(method)) {
         this.hashCodeDefined = true;
       }
       if (this.equalsDefined && this.hashCodeDefined) {
         return;
       }
     }
   }
 }
Exemplo n.º 2
0
 /**
  * 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
  *   <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
  *   <dt>For Object.finalize():
  *   <dd>No override for this method is used.
  *   <dt>For equals():
  *   <dd>The EqualsInterceptor is used to redirect equals() calls to a special handler to this
  *       proxy.
  *   <dt>For methods on the Advised class:
  *   <dd>the AdvisedDispatcher is used to dispatch the call directly to the target
  *   <dt>For advised methods:
  *   <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 DyanmicAdvisedInterceptor is used.
  *   <dt>For non-advised methods:
  *   <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.
  * </dl>
  */
 @Override
 public int accept(Method method) {
   if (AopUtils.isFinalizeMethod(method)) {
     logger.debug("Found finalize() method - using NO_OVERRIDE");
     return NO_OVERRIDE;
   }
   if (!this.advised.isOpaque()
       && method.getDeclaringClass().isInterface()
       && method.getDeclaringClass().isAssignableFrom(Advised.class)) {
     if (logger.isDebugEnabled()) {
       logger.debug("Method is declared on Advised interface: " + method);
     }
     return DISPATCH_ADVISED;
   }
   // We must always proxy equals, to direct calls to this.
   if (AopUtils.isEqualsMethod(method)) {
     logger.debug("Found 'equals' method: " + method);
     return INVOKE_EQUALS;
   }
   // We must always calculate hashCode based on the proxy.
   if (AopUtils.isHashCodeMethod(method)) {
     logger.debug("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.isDebugEnabled()) {
         logger.debug("Must expose proxy on advised method: " + method);
       }
       return AOP_PROXY;
     }
     String key = method.toString();
     // Check to see if we have fixed interceptor to serve this method.
     // Else use the AOP_PROXY.
     if (isStatic && isFrozen && this.fixedInterceptorMap.containsKey(key)) {
       if (logger.isDebugEnabled()) {
         logger.debug("Method has advice and optimisations are enabled: " + method);
       }
       // We know that we are optimising so we can use the FixedStaticChainInterceptors.
       int index = this.fixedInterceptorMap.get(key);
       return (index + this.fixedInterceptorOffset);
     } else {
       if (logger.isDebugEnabled()) {
         logger.debug("Unable to apply any optimisations 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
     // cannot use a dispatcher because the target cannot be released.
     if (exposeProxy || !isStatic) {
       return INVOKE_TARGET;
     }
     Class<?> returnType = method.getReturnType();
     if (targetClass == returnType) {
       if (logger.isDebugEnabled()) {
         logger.debug(
             "Method "
                 + method
                 + "has return type same as target type (may return this) - using INVOKE_TARGET");
       }
       return INVOKE_TARGET;
     } else if (returnType.isPrimitive() || !returnType.isAssignableFrom(targetClass)) {
       if (logger.isDebugEnabled()) {
         logger.debug(
             "Method "
                 + method
                 + " has return type that ensures this cannot be returned- using DISPATCH_TARGET");
       }
       return DISPATCH_TARGET;
     } else {
       if (logger.isDebugEnabled()) {
         logger.debug(
             "Method "
                 + method
                 + "has return type that is assignable from the target type (may return this) - "
                 + "using INVOKE_TARGET");
       }
       return INVOKE_TARGET;
     }
   }
 }
  /**
   * 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[] args) throws Throwable {
    MethodInvocation invocation = null;
    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]) ? Boolean.TRUE : Boolean.FALSE);
      }
      if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
        // The target does not implement the hashCode() method itself.
        return new Integer(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 = null;

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

      // May be <code>null</code>. 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 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);
      }
    }
  }