Open Source Repository

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



org/springframework/aop/support/AopUtils.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.support;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.springframework.aop.Advisor;
import org.springframework.aop.AopInvocationException;
import org.springframework.aop.IntroductionAdvisor;
import org.springframework.aop.IntroductionAwareMethodMatcher;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.aop.SpringProxy;
import org.springframework.aop.TargetClassAware;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;

/**
 * Utility methods for AOP support code.
 * Mainly for internal use within Spring's AOP support.
 *
 <p>See {@link org.springframework.aop.framework.AopProxyUtils} for a
 * collection of framework-specific AOP utility methods which depend
 * on internals of Spring's AOP framework implementation.
 *
 @author Rod Johnson
 @author Juergen Hoeller
 @author Rob Harrop
 @see org.springframework.aop.framework.AopProxyUtils
 */
public abstract class AopUtils {

  /**
   * Check whether the given object is a JDK dynamic proxy or a CGLIB proxy.
   @param object the object to check
   @see #isJdkDynamicProxy
   @see #isCglibProxy
   */
  public static boolean isAopProxy(Object object) {
    return (object instanceof SpringProxy &&
        (Proxy.isProxyClass(object.getClass()) || isCglibProxyClass(object.getClass())));
  }

  /**
   * Check whether the given object is a JDK dynamic proxy.
   @param object the object to check
   @see java.lang.reflect.Proxy#isProxyClass
   */
  public static boolean isJdkDynamicProxy(Object object) {
    return (object instanceof SpringProxy && Proxy.isProxyClass(object.getClass()));
  }

  /**
   * Check whether the given object is a CGLIB proxy.
   @param object the object to check
   */
  public static boolean isCglibProxy(Object object) {
    return (object instanceof SpringProxy && isCglibProxyClass(object.getClass()));
  }

  /**
   * Check whether the specified class is a CGLIB-generated class.
   @param clazz the class to check
   */
  public static boolean isCglibProxyClass(Class<?> clazz) {
    return (clazz != null && isCglibProxyClassName(clazz.getName()));
  }

  /**
   * Check whether the specified class name is a CGLIB-generated class.
   @param className the class name to check
   */
  public static boolean isCglibProxyClassName(String className) {
    return (className != null && className.contains(ClassUtils.CGLIB_CLASS_SEPARATOR));
  }

  /**
   * Determine the target class of the given bean instance which might be an AOP proxy.
   <p>Returns the target class for an AOP proxy and the plain class else.
   @param candidate the instance to check (might be an AOP proxy)
   @return the target class (or the plain class of the given object as fallback;
   * never <code>null</code>)
   @see org.springframework.aop.TargetClassAware#getTargetClass()
   @see org.springframework.aop.framework.AopProxyUtils#ultimateTargetClass(Object)
   */
  public static Class<?> getTargetClass(Object candidate) {
    Assert.notNull(candidate, "Candidate object must not be null");
    Class<?> result = null;
    if (candidate instanceof TargetClassAware) {
      result = ((TargetClassAwarecandidate).getTargetClass();
    }
    if (result == null) {
      result = (isCglibProxy(candidate? candidate.getClass().getSuperclass() : candidate.getClass());
    }
    return result;
  }

  /**
   * Determine whether the given method is an "equals" method.
   @see java.lang.Object#equals
   */
  public static boolean isEqualsMethod(Method method) {
    return ReflectionUtils.isEqualsMethod(method);
  }

  /**
   * Determine whether the given method is a "hashCode" method.
   @see java.lang.Object#hashCode
   */
  public static boolean isHashCodeMethod(Method method) {
    return ReflectionUtils.isHashCodeMethod(method);
  }

  /**
   * Determine whether the given method is a "toString" method.
   @see java.lang.Object#toString()
   */
  public static boolean isToStringMethod(Method method) {
    return ReflectionUtils.isToStringMethod(method);
  }

  /**
   * Determine whether the given method is a "finalize" method.
   @see java.lang.Object#finalize()
   */
  public static boolean isFinalizeMethod(Method method) {
    return (method != null && method.getName().equals("finalize"&&
        method.getParameterTypes().length == 0);
  }

  /**
   * Given a method, which may come from an interface, and a target class used
   * in the current AOP invocation, find the corresponding target method if there
   * is one. E.g. the method may be <code>IFoo.bar()</code> and the target class
   * may be <code>DefaultFoo</code>. In this case, the method may be
   <code>DefaultFoo.bar()</code>. This enables attributes on that method to be found.
   <p><b>NOTE:</b> In contrast to {@link org.springframework.util.ClassUtils#getMostSpecificMethod},
   * this method resolves Java 5 bridge methods in order to retrieve attributes
   * from the <i>original</i> method definition.
   @param method the method to be invoked, which may come from an interface
   @param targetClass the target class for the current invocation.
   * May be <code>null</code> or may not even implement the method.
   @return the specific target method, or the original method if the
   <code>targetClass</code> doesn't implement it or is <code>null</code>
   @see org.springframework.util.ClassUtils#getMostSpecificMethod
   */
  public static Method getMostSpecificMethod(Method method, Class<?> targetClass) {
    Method resolvedMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
    // If we are dealing with method with generic parameters, find the original method.
    return BridgeMethodResolver.findBridgedMethod(resolvedMethod);
  }


  /**
   * Can the given pointcut apply at all on the given class?
   <p>This is an important test as it can be used to optimize
   * out a pointcut for a class.
   @param pc the static or dynamic pointcut to check
   @param targetClass the class to test
   @return whether the pointcut can apply on any method
   */
  public static boolean canApply(Pointcut pc, Class<?> targetClass) {
    return canApply(pc, targetClass, false);
  }

  /**
   * Can the given pointcut apply at all on the given class?
   <p>This is an important test as it can be used to optimize
   * out a pointcut for a class.
   @param pc the static or dynamic pointcut to check
   @param targetClass the class to test
   @param hasIntroductions whether or not the advisor chain
   * for this bean includes any introductions
   @return whether the pointcut can apply on any method
   */
  public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
    if (!pc.getClassFilter().matches(targetClass)) {
      return false;
    }

    MethodMatcher methodMatcher = pc.getMethodMatcher();
    IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
    if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
      introductionAwareMethodMatcher = (IntroductionAwareMethodMatchermethodMatcher;
    }

    Set<Class> classes = new HashSet<Class>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
    classes.add(targetClass);
    for (Class<?> clazz : classes) {
      Method[] methods = clazz.getMethods();
      for (Method method : methods) {
        if ((introductionAwareMethodMatcher != null &&
            introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||
            methodMatcher.matches(method, targetClass)) {
          return true;
        }
      }
    }

    return false;
  }

  /**
   * Can the given advisor apply at all on the given class?
   * This is an important test as it can be used to optimize
   * out a advisor for a class.
   @param advisor the advisor to check
   @param targetClass class we're testing
   @return whether the pointcut can apply on any method
   */
  public static boolean canApply(Advisor advisor, Class<?> targetClass) {
    return canApply(advisor, targetClass, false);
  }

  /**
   * Can the given advisor apply at all on the given class?
   <p>This is an important test as it can be used to optimize out a advisor for a class.
   * This version also takes into account introductions (for IntroductionAwareMethodMatchers).
   @param advisor the advisor to check
   @param targetClass class we're testing
   @param hasIntroductions whether or not the advisor chain for this bean includes
   * any introductions
   @return whether the pointcut can apply on any method
   */
  public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
    if (advisor instanceof IntroductionAdvisor) {
      return ((IntroductionAdvisoradvisor).getClassFilter().matches(targetClass);
    }
    else if (advisor instanceof PointcutAdvisor) {
      PointcutAdvisor pca = (PointcutAdvisoradvisor;
      return canApply(pca.getPointcut(), targetClass, hasIntroductions);
    }
    else {
      // It doesn't have a pointcut so we assume it applies.
      return true;
    }
  }

  /**
   * Determine the sublist of the <code>candidateAdvisors</code> list
   * that is applicable to the given class.
   @param candidateAdvisors the Advisors to evaluate
   @param clazz the target class
   @return sublist of Advisors that can apply to an object of the given class
   * (may be the incoming List as-is)
   */
  public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
    if (candidateAdvisors.isEmpty()) {
      return candidateAdvisors;
    }
    List<Advisor> eligibleAdvisors = new LinkedList<Advisor>();
    for (Advisor candidate : candidateAdvisors) {
      if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
        eligibleAdvisors.add(candidate);
      }
    }
    boolean hasIntroductions = !eligibleAdvisors.isEmpty();
    for (Advisor candidate : candidateAdvisors) {
      if (candidate instanceof IntroductionAdvisor) {
        // already processed
        continue;
      }
      if (canApply(candidate, clazz, hasIntroductions)) {
        eligibleAdvisors.add(candidate);
      }
    }
    return eligibleAdvisors;
  }


  /**
   * Invoke the given target via reflection, as part of an AOP method invocation.
   @param target the target object
   @param method the method to invoke
   @param args the arguments for the method
   @return the invocation result, if any
   @throws Throwable if thrown by the target method
   @throws org.springframework.aop.AopInvocationException in case of a reflection error
   */
  public static Object invokeJoinpointUsingReflection(Object target, Method method, Object[] args)
      throws Throwable {

    // Use reflection to invoke the method.
    try {
      ReflectionUtils.makeAccessible(method);
      return method.invoke(target, args);
    }
    catch (InvocationTargetException ex) {
      // Invoked method threw a checked exception.
      // We must rethrow it. The client won't see the interceptor.
      throw ex.getTargetException();
    }
    catch (IllegalArgumentException ex) {
      throw new AopInvocationException("AOP configuration seems to be invalid: tried calling method [" +
          method + "] on target [" + target + "]", ex);
    }
    catch (IllegalAccessException ex) {
      throw new AopInvocationException("Could not access method [" + method + "]", ex);
    }
  }

}